//
//  NSObject+LKModel.m
//  LKDBHelper
//
//  Created by LJH on 13-4-15.
//  Copyright (c) 2013年 ljh. All rights reserved.
//

#import "NSObject+LKModel.h"
#import "LKDBHelper.h"

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#import <UIKit/UIKit.h>
#define LKDBImage UIImage
#define LKDBColor UIColor
#else
#import <AppKit/AppKit.h>
#define LKDBImage NSImage
#define LKDBColor NSColor
#endif

static char LKModelBase_Key_RowID;
static char LKModelBase_Key_TableName;
static char LKModelBase_Key_Inserting;

@implementation NSObject (LKModel)

+ (LKDBHelper*)getUsingLKDBHelper
{
    ///ios8 能获取系统类的属性了  所以没有办法判断属性数量来区分自定义类和系统类
    ///可能系统类的存取会不正确
    static LKDBHelper* helper;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        helper = [[LKDBHelper alloc] init];
    });
    return helper;
}
#pragma mark Tabel Structure Function 表结构
+ (NSString*)getTableName
{
    return NSStringFromClass(self);
}

+ (NSString*)getPrimaryKey
{
    return @"rowid";
}

+ (NSArray*)getPrimaryKeyUnionArray
{
    return nil;
}

+ (void)columnAttributeWithProperty:(LKDBProperty*)property
{
    //overwrite
}
#pragma 属性
- (void)setRowid:(NSInteger)rowid
{
    objc_setAssociatedObject(self, &LKModelBase_Key_RowID, [NSNumber numberWithInteger:rowid], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)rowid
{
    return [objc_getAssociatedObject(self, &LKModelBase_Key_RowID) integerValue];
}

- (void)setDb_tableName:(NSString*)db_tableName
{
    objc_setAssociatedObject(self, &LKModelBase_Key_TableName, db_tableName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString*)db_tableName
{
    NSString* tableName = objc_getAssociatedObject(self, &LKModelBase_Key_TableName);
    if (tableName.length == 0) {
        tableName = [self.class getTableName];
    }
    return tableName;
}
- (BOOL)db_inserting
{
    return [objc_getAssociatedObject(self, &LKModelBase_Key_Inserting) boolValue];
}
- (void)setDb_inserting:(BOOL)db_inserting
{
    NSNumber* number = nil;
    if (db_inserting) {
        number = [NSNumber numberWithBool:YES];
    }
    objc_setAssociatedObject(self, &LKModelBase_Key_Inserting, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma 无关紧要的
+ (NSString*)getDBImagePathWithName:(NSString*)filename
{
    NSString* dir = [NSString stringWithFormat:@"dbimg/%@", NSStringFromClass(self)];
    return [LKDBUtils getPathForDocuments:filename inDir:dir];
}
+ (NSString*)getDBDataPathWithName:(NSString*)filename
{
    NSString* dir = [NSString stringWithFormat:@"dbdata/%@", NSStringFromClass(self)];
    return [LKDBUtils getPathForDocuments:filename inDir:dir];
}
+ (NSDictionary*)getTableMapping
{
    return nil;
}
#pragma mark - Table Data Function 表数据
+ (NSDateFormatter*)getModelDateFormatter
{
    return nil;
}

///get
- (id)modelGetValue:(LKDBProperty*)property
{
    id value = [self valueForKey:property.propertyName];
    id returnValue = value;
    if (value == nil) {
        return nil;
    }
    else if ([value isKindOfClass:[NSString class]]) {
        returnValue = value;
    }
    else if ([value isKindOfClass:[NSNumber class]]) {
        returnValue = [[LKDBUtils numberFormatter] stringFromNumber:value];
    }
    else if ([value isKindOfClass:[NSDate class]]) {
        NSDateFormatter* formatter = [self.class getModelDateFormatter];
        if (formatter) {
            returnValue = [formatter stringFromDate:value];
        }
        else {
            returnValue = [LKDBUtils stringWithDate:value];
        }
        returnValue = [returnValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    else if ([value isKindOfClass:[LKDBColor class]]) {
        LKDBColor* color = value;
        CGFloat r, g, b, a;
        [color getRed:&r green:&g blue:&b alpha:&a];
        returnValue = [NSString stringWithFormat:@"%.3f,%.3f,%.3f,%.3f", r, g, b, a];
    }
    else if ([value isKindOfClass:[NSValue class]]) {
        NSString* columnType = property.propertyType;
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
        if ([columnType isEqualToString:@"CGRect"]) {
            returnValue = NSStringFromCGRect([value CGRectValue]);
        }
        else if ([columnType isEqualToString:@"CGPoint"]) {
            returnValue = NSStringFromCGPoint([value CGPointValue]);
        }
        else if ([columnType isEqualToString:@"CGSize"]) {
            returnValue = NSStringFromCGSize([value CGSizeValue]);
        }
        else if ([columnType isEqualToString:@"_NSRange"]) {
            returnValue = NSStringFromRange([value rangeValue]);
        }
#else
        if ([columnType hasSuffix:@"Rect"]) {
            returnValue = NSStringFromRect([value rectValue]);
        }
        else if ([columnType hasSuffix:@"Point"]) {
            returnValue = NSStringFromPoint([value pointValue]);
        }
        else if ([columnType hasSuffix:@"Size"]) {
            returnValue = NSStringFromSize([value sizeValue]);
        }
        else if ([columnType hasSuffix:@"Range"]) {
            returnValue = NSStringFromRange([value rangeValue]);
        }
#endif
    }
    else if ([value isKindOfClass:[LKDBImage class]]) {
        long random = arc4random();
        long date = [[NSDate date] timeIntervalSince1970];
        NSString* filename = [NSString stringWithFormat:@"img%ld%ld", date & 0xFFFFF, random & 0xFFF];

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
        NSData* datas = UIImageJPEGRepresentation(value, 1);
#else
        [value lockFocus];
        NSBitmapImageRep* srcImageRep = [NSBitmapImageRep imageRepWithData:[value TIFFRepresentation]];
        NSData* datas = [srcImageRep representationUsingType:NSJPEGFileType properties:@{}];
        [value unlockFocus];
#endif
        [datas writeToFile:[self.class getDBImagePathWithName:filename]
                atomically:YES];

        returnValue = filename;
    }
    else if ([value isKindOfClass:[NSData class]]) {
        long random = arc4random();
        long date = [[NSDate date] timeIntervalSince1970];
        NSString* filename = [NSString stringWithFormat:@"data%ld%ld", date & 0xFFFFF, random & 0xFFF];

        [value writeToFile:[self.class getDBDataPathWithName:filename] atomically:YES];

        returnValue = filename;
    }
    else {
        if ([value isKindOfClass:[NSArray class]]) {
            returnValue = [self db_jsonObjectFromArray:value];
        }
        else if ([value isKindOfClass:[NSDictionary class]]) {
            returnValue = [self db_jsonObjectFromDictionary:value];
        }
        else {
            returnValue = [self db_jsonObjectFromModel:value];
        }
        returnValue = [self db_jsonStringFromObject:returnValue];
    }

    return returnValue;
}

///set
- (void)modelSetValue:(LKDBProperty*)property value:(id)value
{
    ///参试获取属性的Class
    Class columnClass = NSClassFromString(property.propertyType);

    id modelValue = nil;

    if (columnClass == nil) {
        ///当找不到 class 时，就是 基础类型 int,float CGRect 之类的

        NSString* columnType = property.propertyType;
        if ([LKSQL_Convert_FloatType rangeOfString:columnType].location != NSNotFound) {
            modelValue = [[LKDBUtils numberFormatter] numberFromString:value];
        }
        else if ([LKSQL_Convert_IntType rangeOfString:columnType].location != NSNotFound) {
            modelValue = [[LKDBUtils numberFormatter] numberFromString:value];
        }
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
        else if ([columnType isEqualToString:@"CGRect"]) {
            CGRect rect = CGRectFromString(value);
            modelValue = [NSValue valueWithCGRect:rect];
        }
        else if ([columnType isEqualToString:@"CGPoint"]) {
            CGPoint point = CGPointFromString(value);
            modelValue = [NSValue valueWithCGPoint:point];
        }
        else if ([columnType isEqualToString:@"CGSize"]) {
            CGSize size = CGSizeFromString(value);
            modelValue = [NSValue valueWithCGSize:size];
        }
        else if ([columnType isEqualToString:@"_NSRange"]) {
            NSRange range = NSRangeFromString(value);
            modelValue = [NSValue valueWithRange:range];
        }
#else
        else if ([columnType hasSuffix:@"Rect"]) {
            NSRect rect = NSRectFromString(value);
            modelValue = [NSValue valueWithRect:rect];
        }
        else if ([columnType hasSuffix:@"Point"]) {
            NSPoint point = NSPointFromString(value);
            modelValue = [NSValue valueWithPoint:point];
        }
        else if ([columnType hasSuffix:@"Size"]) {
            NSSize size = NSSizeFromString(value);
            modelValue = [NSValue valueWithSize:size];
        }
        else if ([columnType hasSuffix:@"Range"]) {
            NSRange range = NSRangeFromString(value);
            modelValue = [NSValue valueWithRange:range];
        }
#endif
        ///如果都没有值 默认给个0
        if (modelValue == nil) {
            modelValue = [NSNumber numberWithInt:0];
        }
    }
    else if ([value length] == 0) {
        //为了不继续遍历
    }
    else if ([columnClass isSubclassOfClass:[NSString class]]) {
        modelValue = value;
    }
    else if ([columnClass isSubclassOfClass:[NSNumber class]]) {
        modelValue = [[LKDBUtils numberFormatter] numberFromString:value];
    }
    else if ([columnClass isSubclassOfClass:[NSDate class]]) {
        NSString* datestr = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSDateFormatter* formatter = [self.class getModelDateFormatter];
        if (formatter) {
            modelValue = [formatter dateFromString:datestr];
        }
        else {
            modelValue = [LKDBUtils dateWithString:datestr];
        }
    }
    else if ([columnClass isSubclassOfClass:[LKDBColor class]]) {
        NSString* color = value;
        NSArray* array = [color componentsSeparatedByString:@","];
        float r, g, b, a;
        r = [[array objectAtIndex:0] floatValue];
        g = [[array objectAtIndex:1] floatValue];
        b = [[array objectAtIndex:2] floatValue];
        a = [[array objectAtIndex:3] floatValue];

        modelValue = [LKDBColor colorWithRed:r green:g blue:b alpha:a];
    }
    else if ([columnClass isSubclassOfClass:[LKDBImage class]]) {
        NSString* filename = value;
        NSString* filepath = [self.class getDBImagePathWithName:filename];
        if ([LKDBUtils isFileExists:filepath]) {
            LKDBImage* img = [[LKDBImage alloc] initWithContentsOfFile:filepath];
            modelValue = img;
        }
        else {
            modelValue = nil;
        }
    }
    else if ([columnClass isSubclassOfClass:[NSData class]]) {
        NSString* filename = value;
        NSString* filepath = [self.class getDBDataPathWithName:filename];
        if ([LKDBUtils isFileExists:filepath]) {
            NSData* data = [NSData dataWithContentsOfFile:filepath];
            modelValue = data;
        }
        else {
            modelValue = nil;
        }
    }
    else {
        modelValue = [self db_modelWithJsonValue:value];
        if ([modelValue isKindOfClass:columnClass] == NO) {
            modelValue = nil;
        }
    }

    [self setValue:modelValue forKey:property.propertyName];
}
#pragma mark - 对 model NSArray NSDictionary 进行支持
- (id)db_jsonObjectFromDictionary:(NSDictionary*)dic
{
    if ([NSJSONSerialization isValidJSONObject:dic]) {
        NSDictionary* bomb = @{ LKDB_TypeKey : LKDB_TypeKey_JSON, LKDB_ValueKey : dic };
        return bomb;
    }
    else {
        NSMutableDictionary* toDic = [NSMutableDictionary dictionary];
        NSArray* allKeys = dic.allKeys;
        for (NSInteger i = 0; i < allKeys.count; i++) {
            NSString* key = [allKeys objectAtIndex:i];
            id obj = [dic objectForKey:key];
            id jsonObject = [self db_jsonObjectWithObject:obj];
            if (jsonObject) {
                [toDic setObject:jsonObject forKey:key];
            }
        }

        if (toDic.count) {
            NSDictionary* bomb = @{ LKDB_TypeKey : LKDB_TypeKey_Combo, LKDB_ValueKey : toDic };
            return bomb;
        }
    }
    return nil;
}
- (id)db_jsonObjectFromArray:(NSArray*)array
{
    if ([NSJSONSerialization isValidJSONObject:array]) {
        NSDictionary* bomb = @{ LKDB_TypeKey : LKDB_TypeKey_JSON, LKDB_ValueKey : array };
        return bomb;
    }
    else {
        NSMutableArray* toArray = [NSMutableArray array];
        NSInteger count = array.count;
        for (NSInteger i = 0; i < count; i++) {
            id obj = [array objectAtIndex:i];
            id jsonObject = [self db_jsonObjectWithObject:obj];
            if (jsonObject) {
                [toArray addObject:jsonObject];
            }
        }

        if (toArray.count) {
            NSDictionary* bomb = @{ LKDB_TypeKey : LKDB_TypeKey_Combo, LKDB_ValueKey : toArray };
            return bomb;
        }
    }
    return nil;
}
///目前只支持 model、NSString、NSNumber 简单类型
- (id)db_jsonObjectWithObject:(id)obj
{
    id jsonObject = nil;
    if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
        jsonObject = obj;
    }
    else if ([obj isKindOfClass:[NSDate class]]) {
        NSString* dateString = nil;
        NSDateFormatter* formatter = [self.class getModelDateFormatter];
        if (formatter) {
            dateString = [formatter stringFromDate:obj];
        }
        else {
            dateString = [LKDBUtils stringWithDate:obj];
        }
        dateString = [dateString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        if (dateString.length > 0) {
            jsonObject = @{ LKDB_TypeKey : LKDB_TypeKey_Date, LKDB_ValueKey : dateString };
        }
    }
    else if ([obj isKindOfClass:[NSArray class]]) {
        jsonObject = [self db_jsonObjectFromArray:obj];
    }
    else if ([obj isKindOfClass:[NSDictionary class]]) {
        jsonObject = [self db_jsonObjectFromArray:obj];
    }
    else {
        jsonObject = [self db_jsonObjectFromModel:obj];
    }

    if (jsonObject == nil) {
        jsonObject = [obj description];
    }
    return jsonObject;
}

- (id)db_jsonObjectFromModel:(NSObject*)model
{
    Class clazz = model.class;
    NSDictionary* jsonObject = nil;
    if (model.rowid > 0) {
        [model updateToDB];
        jsonObject = [self db_readInfoWithModel:model class:clazz];
    }
    else {
        if (model.db_inserting == NO && [clazz getModelInfos] > 0) {
            BOOL success = [model saveToDB];
            if (success) {
                jsonObject = [self db_readInfoWithModel:model class:clazz];
            }
        }
        else {
            NSAssert(NO, @"目前LKDB 还不支持 循环引用。  比如 A 持有 B，   B 持有 A，这种的存储");
        }
    }
    return jsonObject;
}
- (NSDictionary*)db_readInfoWithModel:(NSObject*)model class:(Class)clazz
{
    NSMutableDictionary* jsonObject = [NSMutableDictionary dictionary];
    [jsonObject setObject:LKDB_TypeKey_Model forKey:LKDB_TypeKey];
    [jsonObject setObject:model.db_tableName forKey:LKDB_TableNameKey];
    [jsonObject setObject:NSStringFromClass(clazz) forKey:LKDB_ClassKey];
    [jsonObject setObject:@(model.rowid) forKey:LKDB_RowIdKey];

    NSDictionary* dic = [model db_getPrimaryKeysValues];
    if (dic.count > 0 && [NSJSONSerialization isValidJSONObject:dic]) {
        [jsonObject setObject:dic forKey:LKDB_PValueKey];
    }
    return jsonObject;
}

- (NSString*)db_jsonStringFromObject:(NSObject*)jsonObject
{
    if (jsonObject && [NSJSONSerialization isValidJSONObject:jsonObject]) {
        NSData* data = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:nil];
        if (data.length > 0) {
            NSString* jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            return jsonString;
        }
    }
    return nil;
}
- (id)db_modelWithJsonValue:(id)value
{
    NSData* jsonData = nil;
    if ([value isKindOfClass:[NSString class]]) {
        jsonData = [value dataUsingEncoding:NSUTF8StringEncoding];
    }
    else if ([value isKindOfClass:[NSData class]]) {
        jsonData = value;
    }

    if (jsonData.length > 0) {
        NSDictionary* jsonDic = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
        return [self db_objectWithDictionary:jsonDic];
    }
    return nil;
}
- (id)db_objectWithArray:(NSArray*)array
{
    NSMutableArray* toArray = nil;

    NSInteger count = array.count;
    for (NSInteger i = 0; i < count; i++) {
        id value = [array objectAtIndex:i];
        if ([value isKindOfClass:[NSDictionary class]]) {
            value = [self db_objectWithDictionary:value];
        }
        else if ([value isKindOfClass:[NSArray class]]) {
            value = [self db_objectWithArray:value];
        }

        if (value) {
            if (toArray == nil) {
                toArray = [NSMutableArray array];
            }
            [toArray addObject:value];
        }
    }

    return toArray;
}
- (id)db_objectWithDictionary:(NSDictionary*)dic
{
    if (dic.count == 0) {
        return nil;
    }
    NSString* type = [dic objectForKey:LKDB_TypeKey];
    if (type) {
        if ([type isEqualToString:LKDB_TypeKey_Model]) {
            Class clazz = NSClassFromString([dic objectForKey:LKDB_ClassKey]);
            NSInteger rowid = [[dic objectForKey:LKDB_RowIdKey] integerValue];
            NSString* tableName = [dic objectForKey:LKDB_TableNameKey];

            NSString* where = nil;

            NSString* rowCountWhere = [NSString stringWithFormat:@"select count(rowid) from %@ where rowid=%ld limit 1", tableName, (long)rowid];
            NSInteger result = [[[clazz getUsingLKDBHelper] executeScalarWithSQL:rowCountWhere arguments:nil] integerValue];
            if (result > 0) {
                where = [NSString stringWithFormat:@"select rowid,* from %@ where rowid=%ld limit 1", tableName, (long)rowid];
            }
            else {
                NSDictionary* pv = [dic objectForKey:LKDB_PValueKey];
                if (pv.count > 0) {
                    BOOL isNeedAddDot = NO;
                    NSMutableString* sb = [NSMutableString stringWithFormat:@"select rowid,* from %@ where", tableName];

                    NSArray* allKeys = pv.allKeys;
                    for (NSString* key in allKeys) {
                        id obj = [pv objectForKey:key];
                        if (isNeedAddDot) {
                            [sb appendString:@" and"];
                        }
                        [sb appendFormat:@" %@ = '%@'", key, obj];

                        isNeedAddDot = YES;
                    }

                    [sb appendString:@" limit 1"];

                    where = [NSString stringWithString:sb];
                }
            }

            if (where) {
                NSArray* array = [[clazz getUsingLKDBHelper] searchWithSQL:where toClass:clazz];
                if (array.count > 0) {
                    NSObject* result = [array objectAtIndex:0];
                    result.db_tableName = tableName;
                    return result;
                }
            }
        }
        else if ([type isEqualToString:LKDB_TypeKey_JSON]) {
            id value = [dic objectForKey:LKDB_ValueKey];
            return value;
        }
        else if ([type isEqualToString:LKDB_TypeKey_Combo]) {
            id value = [dic objectForKey:LKDB_ValueKey];
            if ([value isKindOfClass:[NSArray class]]) {
                return [self db_objectWithArray:value];
            }
            else if ([value isKindOfClass:[NSDictionary class]]) {
                return [self db_objectWithDictionary:value];
            }
            else {
                return value;
            }
        }
        else if ([type isEqualToString:LKDB_TypeKey_Date]) {
            NSString* datestr = [dic objectForKey:LKDB_ValueKey];
            NSDateFormatter* formatter = [self.class getModelDateFormatter];
            if (formatter) {
                return [formatter dateFromString:datestr];
            }
            else {
                return [LKDBUtils dateWithString:datestr];
            }
        }
    }
    else {
        NSArray* allKeys = dic.allKeys;
        NSMutableDictionary* toDic = [NSMutableDictionary dictionary];
        for (NSInteger i = 0; i < allKeys.count; i++) {
            NSString* key = [allKeys objectAtIndex:i];
            id value = [dic objectForKey:key];

            id saveObj = value;
            if ([value isKindOfClass:[NSArray class]]) {
                saveObj = [self db_objectWithArray:value];
            }
            else if ([value isKindOfClass:[NSDictionary class]]) {
                saveObj = [self db_objectWithDictionary:value];
            }

            if (saveObj) {
                [toDic setObject:saveObj forKey:key];
            }
        }
        return toDic;
    }
    return nil;
}
#pragma mark - your can overwrite
- (void)setNilValueForKey:(NSString*)key
{
    NSLog(@"nil 这种设置到了 int 等基础类型中");
}
- (id)valueForUndefinedKey:(NSString*)key
{
    NSLog(@"你有get方法没实现，key:%@", key);
    return nil;
}
- (void)setValue:(id)value forUndefinedKey:(NSString*)key
{
    NSLog(@"你有set方法没实现，key:%@", key);
}

#pragma mark -
- (void)userSetValueForModel:(LKDBProperty*)property value:(id)value
{
}
- (id)userGetValueForModel:(LKDBProperty*)property
{
    return nil;
}

- (NSDictionary*)db_getPrimaryKeysValues
{
    LKModelInfos* infos = [self.class getModelInfos];
    NSArray* array = infos.primaryKeys;
    NSMutableDictionary* dic = [NSMutableDictionary dictionary];
    for (NSString* pname in array) {
        LKDBProperty* property = [infos objectWithSqlColumnName:pname];
        id value = nil;
        if ([property.type isEqualToString:LKSQL_Mapping_UserCalculate]) {
            value = [self userGetValueForModel:property];
        }
        else {
            value = [self modelGetValue:property];
        }
        if (value) {
            [dic setObject:value forKey:property.sqlColumnName];
        }
    }
    return dic;
}
//主键值 是否为空
- (BOOL)singlePrimaryKeyValueIsEmpty
{
    LKDBProperty* property = [self singlePrimaryKeyProperty];
    if (property) {
        id pkvalue = [self singlePrimaryKeyValue];
        if ([property.sqlColumnType isEqualToString:LKSQL_Type_Int]) {
            if ([pkvalue isKindOfClass:[NSString class]]) {
                if ([LKDBUtils checkStringIsEmpty:pkvalue])
                    return YES;

                if ([pkvalue integerValue] == 0)
                    return YES;

                return NO;
            }
            if ([pkvalue isKindOfClass:[NSNumber class]]) {
                if ([pkvalue integerValue] == 0)
                    return YES;
                else
                    return NO;
            }
            return YES;
        }
        else {
            return (pkvalue == nil);
        }
    }
    return NO;
}
- (LKDBProperty*)singlePrimaryKeyProperty
{
    LKModelInfos* infos = [self.class getModelInfos];
    if (infos.primaryKeys.count == 1) {
        NSString* name = [infos.primaryKeys objectAtIndex:0];
        return [infos objectWithSqlColumnName:name];
    }
    return nil;
}
- (id)singlePrimaryKeyValue
{
    LKDBProperty* property = [self singlePrimaryKeyProperty];
    if (property) {
        if ([property.type isEqualToString:LKSQL_Mapping_UserCalculate]) {
            return [self userGetValueForModel:property];
        }
        else {
            return [self modelGetValue:property];
        }
    }
    return nil;
}
+ (NSString*)db_rowidAliasName
{
    LKModelInfos* infos = [self getModelInfos];
    if (infos.primaryKeys.count == 1) {
        NSString* primaryType = [infos objectWithSqlColumnName:[infos.primaryKeys lastObject]].sqlColumnType;
        if ([primaryType isEqualToString:LKSQL_Type_Int]) {
            return [infos.primaryKeys lastObject];
        }
    }
    return nil;
}

#pragma mark - get model property info
+ (LKModelInfos*)getModelInfos
{
    static __strong NSMutableDictionary* oncePropertyDic;
    static __strong NSRecursiveLock* lock;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lock = [[NSRecursiveLock alloc] init];
        oncePropertyDic = [[NSMutableDictionary alloc] initWithCapacity:8];
    });

    LKModelInfos* infos;
    [lock lock];

    infos = [oncePropertyDic objectForKey:NSStringFromClass(self)];
    if (infos == nil) {
        NSMutableArray* pronames = [NSMutableArray array];
        NSMutableArray* protypes = [NSMutableArray array];
        NSDictionary* keymapping = [self getTableMapping];

        if ([self isContainSelf] && [self class] != [NSObject class]) {
            [self getSelfPropertys:pronames protypes:protypes];
        }

        NSArray* pkArray = [self getPrimaryKeyUnionArray];
        if (pkArray.count == 0) {
            pkArray = nil;
            NSString* pk = [self getPrimaryKey];
            if ([LKDBUtils checkStringIsEmpty:pk] == NO) {
                pkArray = [NSArray arrayWithObject:pk];
            }
        }
        if ([self isContainParent] && [self superclass] != [NSObject class]) {
            LKModelInfos* superInfos = [[self superclass] getModelInfos];
            for (NSInteger i = 0; i < superInfos.count; i++) {
                LKDBProperty* db_p = [superInfos objectWithIndex:i];
                if (db_p.propertyName && db_p.propertyType && [db_p.propertyName isEqualToString:@"rowid"] == NO) {
                    [pronames addObject:db_p.propertyName];
                    [protypes addObject:db_p.propertyType];
                }
            }
        }
        if (pronames.count > 0) {
            infos = [[LKModelInfos alloc] initWithKeyMapping:keymapping propertyNames:pronames propertyType:protypes primaryKeys:pkArray];
        }
        else {
            infos = [[LKModelInfos alloc] init];
        }

        [oncePropertyDic setObject:infos forKey:NSStringFromClass(self)];
    }

    [lock unlock];
    return infos;
}

+ (BOOL)isContainParent
{
    return NO;
}

+ (BOOL)isContainSelf
{
    return YES;
}

/**
 *	@brief	获取自身的属性
 *
 *	@param 	pronames 	保存属性名称
 *	@param 	protypes 	保存属性类型
 */
+ (void)getSelfPropertys:(NSMutableArray*)pronames protypes:(NSMutableArray*)protypes
{
    unsigned int outCount = 0, i = 0;
    objc_property_t* properties = class_copyPropertyList(self, &outCount);

    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        NSString* propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];

        //取消rowid 的插入 //子类 已重载的属性 取消插入
        if (propertyName.length == 0 || [propertyName isEqualToString:@"rowid"] ||
            [pronames indexOfObject:propertyName] != NSNotFound) {
            continue;
        }
        NSString* propertyType = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];

        ///过滤只读属性
        if ([propertyType rangeOfString:@",R,"].length > 0 || [propertyType hasSuffix:@",R"]) {
            NSString* firstWord = [[propertyName substringToIndex:1] uppercaseString];
            NSString* otherWord = [propertyName substringFromIndex:1];
            NSString* setMethodString = [NSString stringWithFormat:@"set%@%@:", firstWord, otherWord];
            SEL setSEL = NSSelectorFromString(setMethodString);
            ///有set方法就不过滤了
            if ([self instancesRespondToSelector:setSEL] == NO) {
                continue;
            }
        }

        /*
         c char
         i int
         l long
         s short
         d double
         f float
         @ id //指针 对象
         ...  BOOL 获取到的表示 方式是 char
         .... ^i 表示  int*  一般都不会用到
         */

        NSString* propertyClassName = nil;
        if ([propertyType hasPrefix:@"T@"]) {

            NSRange range = [propertyType rangeOfString:@","];
            if (range.location > 4 && range.location <= propertyType.length) {
                range = NSMakeRange(3, range.location - 4);
                propertyClassName = [propertyType substringWithRange:range];
                if ([propertyClassName hasSuffix:@">"]) {
                    NSRange categoryRange = [propertyClassName rangeOfString:@"<"];
                    if (categoryRange.length > 0) {
                        propertyClassName = [propertyClassName substringToIndex:categoryRange.location];
                    }
                }
            }
        }
        else if ([propertyType hasPrefix:@"T{"]) {
            NSRange range = [propertyType rangeOfString:@"="];
            if (range.location > 2 && range.location <= propertyType.length) {
                range = NSMakeRange(2, range.location - 2);
                propertyClassName = [propertyType substringWithRange:range];
            }
        }
        else {
            propertyType = [propertyType lowercaseString];
            if ([propertyType hasPrefix:@"ti"] || [propertyType hasPrefix:@"tb"]) {
                propertyClassName = @"int";
            }
            else if ([propertyType hasPrefix:@"tf"]) {
                propertyClassName = @"float";
            }
            else if ([propertyType hasPrefix:@"td"]) {
                propertyClassName = @"double";
            }
            else if ([propertyType hasPrefix:@"tl"] || [propertyType hasPrefix:@"tq"]) {
                propertyClassName = @"long";
            }
            else if ([propertyType hasPrefix:@"tc"]) {
                propertyClassName = @"char";
            }
            else if ([propertyType hasPrefix:@"ts"]) {
                propertyClassName = @"short";
            }
        }

        if ([LKDBUtils checkStringIsEmpty:propertyClassName]) {
            ///没找到具体的属性就放弃
            continue;
        }
        ///添加属性
        [pronames addObject:propertyName];
        [protypes addObject:propertyClassName];
    }
    free(properties);
    if ([self isContainParent] && [self superclass] != [NSObject class]) {
        [[self superclass] getSelfPropertys:pronames protypes:protypes];
    }
}

#pragma mark - log all property
- (NSMutableString*)getAllPropertysString
{
    Class clazz = [self class];
    NSMutableString* sb = [NSMutableString stringWithFormat:@"\n <%@> :\n", NSStringFromClass(clazz)];
    [sb appendFormat:@"rowid : %ld\n", (long)self.rowid];
    [self mutableString:sb appendPropertyStringWithClass:clazz containParent:YES];
    return sb;
}
- (NSString*)printAllPropertys
{
    return [self printAllPropertysIsContainParent:NO];
}
- (NSString*)printAllPropertysIsContainParent:(BOOL)containParent
{
#ifdef DEBUG
    Class clazz = [self class];
    NSMutableString* sb = [NSMutableString stringWithFormat:@"\n <%@> :\n", NSStringFromClass(clazz)];
    [sb appendFormat:@"rowid : %ld\n", (long)self.rowid];
    [self mutableString:sb appendPropertyStringWithClass:clazz containParent:containParent];
    NSLog(@"%@", sb);
    return sb;
#else
    return @"";
#endif
}
- (void)mutableString:(NSMutableString*)sb appendPropertyStringWithClass:(Class)clazz containParent:(BOOL)containParent
{
    if (clazz == [NSObject class]) {
        return;
    }
    unsigned int outCount = 0, i = 0;
    objc_property_t* properties = class_copyPropertyList(clazz, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        NSString* propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        [sb appendFormat:@" %@ : %@ \n", propertyName, [self valueForKey:propertyName]];
    }
    free(properties);
    if (containParent) {
        [self mutableString:sb appendPropertyStringWithClass:clazz.superclass containParent:containParent];
    }
}

@end