//
//  CWDatabase.m
//  CWDB
//
//  Created by ChavezChen on 2017/12/2.
//  Copyright © 2017年 Chavez. All rights reserved.
//

#import "CWDatabase.h"
#import <sqlite3.h>

#define kCWDBCachePath NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject

//#define kCWDBCachePath @"/Users/mac/Desktop"

@interface CWDatabase ()

@end

@implementation CWDatabase

static sqlite3 *cw_database = nil;

static NSTimeInterval _startBusyRetryTime;


+ (BOOL)execSQL:(NSString *)sql uid:(NSString *)uid {
    
    if (!cw_database) {
        if (![self openDB:uid]) {
            return NO;
        }
    }
    
    char *errmsg = nil;
    int result = sqlite3_exec(cw_database, sql.UTF8String, nil, nil, &errmsg);
    
    if (result != SQLITE_OK) {
        NSLog(@"exec SQL(%@) error : %s",sql,errmsg);
        sqlite3_free(errmsg);
        return NO;
    }
    return YES;
}

+ (BOOL)execSqls:(NSArray <NSString *>*)sqls uid:(NSString *)uid {
    [self beginTransaction:uid];
    
    for (NSString *sql in sqls) {
        BOOL result = [self execSQL:sql uid:uid];
        if (result == NO) {
            [self rollBackTransaction:uid];
            return NO;
        }
    }
    [self commitTransaction:uid];
    return YES;
}


+ (NSMutableArray <NSMutableDictionary *>*)querySql:(NSString *)sql uid:(NSString *)uid {
    if (!cw_database) {
        if (![self openDB:uid]) {
            return nil;
        }
    }
    
    sqlite3_stmt *ppStmt     = 0x00;
    if (sqlite3_prepare_v2(cw_database, sql.UTF8String, -1, &ppStmt, nil) != SQLITE_OK) {
        NSLog(@"查询准备语句编译失败");
        return nil;
    }

    NSMutableArray *rowDicArray = [NSMutableArray array];
    while (sqlite3_step(ppStmt) == SQLITE_ROW) {
        int columnCount = sqlite3_column_count(ppStmt);
        NSMutableDictionary *rowDict = [NSMutableDictionary dictionary];
        for (int i = 0; i < columnCount; i++) {
            NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name(ppStmt, i)];
            int type = sqlite3_column_type(ppStmt, i);
            id value = nil;
            switch (type) {
                case SQLITE_INTEGER:
                    value = @(sqlite3_column_int(ppStmt, i));
                    break;
                case SQLITE_FLOAT:
                    value = @(sqlite3_column_double(ppStmt, i));
                    
                    break;
                case SQLITE_BLOB:
                    value = CFBridgingRelease(sqlite3_column_blob(ppStmt, i));
                    break;
                case SQLITE_NULL:
                    value = @"";
                    break;
                case SQLITE3_TEXT:
                    value = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(ppStmt, i)];
                    break;
                    
                default:
                    break;
            }
            [rowDict setValue:value forKey:columnName];
        }
        [rowDicArray addObject:rowDict];
    }

    sqlite3_finalize(ppStmt);
    
    return rowDicArray;
}

static int CWDBBusyCallBack(void *f, int count) {
    if (count == 0) {
        _startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate];
        return 1;
    }
    
    NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - _startBusyRetryTime;
    if (delta < 2) {
        int actualSleepInMilliseconds = sqlite3_sleep(100);
        if (actualSleepInMilliseconds != 100) {
     
        }
        return 1;
    }
    return 0;
}


#pragma 私有方法
+ (BOOL)openDB:(NSString *)uid {
    NSString *dbName = @"CWDB.sqlite";
    if (uid.length != 0) {
        dbName = [NSString stringWithFormat:@"%@.sqlite", uid];
    }
    NSString *dbPath = [kCWDBCachePath stringByAppendingPathComponent:dbName];
    int result = sqlite3_open(dbPath.UTF8String, &cw_database);
    if (result != SQLITE_OK) {
        NSLog(@"打开数据库失败! : %d",result);
        return NO;
    }
    sqlite3_busy_handler(cw_database, &CWDBBusyCallBack, (void *)(cw_database));
    
    return YES;
}

+ (void)closeDB {
    if (cw_database) {
        sqlite3_close(cw_database);
        cw_database = nil;
    }
}

#pragma mark - 事务
+ (void)beginTransaction:(NSString *)uid {
    [self execSQL:@"BEGIN TRANSACTION" uid:uid];
}

+ (void)commitTransaction:(NSString *)uid {
     [self execSQL:@"COMMIT TRANSACTION" uid:uid];
}

+ (void)rollBackTransaction:(NSString *)uid {
     [self execSQL:@"ROLLBACK TRANSACTION" uid:uid];
}


@end
