//
//  main.c
//  AI003
//
//  Created by 西田　耀 on 13/01/28.
//  Copyright (c) 2013年 Hikaru Nishida. All rights reserved.
//

//
//Include headers
//

#include <stdio.h>
#include "core.h"

//
//Define static values
//

AI_WorkingSet WorkingSet;

int main(int argc, const char * argv[])
{
    //エントリポイント
    int i, i_max, j, count;
    CHNLIB_String *input, *temp;
    CHNLIB_UIPArray *separated, *sorted;
    int passthink;
    FILE *readfp, *writefp;
    
    CHNLIB_Environment_SetCurrentWorkingDirectory(argv[0]);
    
    AI_System_InitializeSystemWorkingSet();
    
    AI_System_LoadMemory(AI_CONFIG_FILE_NAME);
    
    readfp = NULL;
    writefp = NULL;

    for(count = 0; ; count++){
        passthink = False;
        if(readfp == NULL){
            input = CHNLIB_ReadLine(stdin);
        } else{
            input = CHNLIB_ReadLine(readfp);
            if(input == NULL){
                readfp = NULL;
            } else{
                puts(CHNLIB_String_GetReferencePointerOfCString(input));
            }
        }

        if(CHNLIB_String_CompareStringWithCString(input, "::")){
            //システムコマンド解釈
            passthink = True;
            separated = CHNLIB_UIPArray_Initialize();
            CHNLIB_UIPArray_GetSeparatedStringByUIPArray(&separated, WorkingSet.SystemWordList0, input);
            
            temp = CHNLIB_UIPArray_GetPointerByIndex(separated, 2);
            if(temp != NULL){
                if(CHNLIB_String_CompareStringWithCString(temp, "info")){
                    printf("AI internal information:\n");
                } else if(CHNLIB_String_CompareStringWithCString(temp, "exit")){
                    break;
                } else if(CHNLIB_String_CompareStringWithCString(temp, "wordlist")){
                    temp = CHNLIB_UIPArray_GetPointerByIndex(separated, 4);
                    if(temp == NULL){
                        //そのまま表示
                        sorted = WorkingSet.RootWordList;
                        retain(sorted);
                    } else if(CHNLIB_String_CompareStringWithCString(temp, "count")){
                        //回数カウント順
                        sorted = CHNLIB_UIPArray_GetSortedInDescendingOrderByData32(WorkingSet.RootWordList);
                    } else if(CHNLIB_String_CompareStringWithCString(temp, "length")){
                        //文字列長順
                        sorted = AI_Think_WordList_GetSortedByLength(WorkingSet.RootWordList);
                    } else{
                        sorted = NULL;
                    }
                    
                    i_max = CHNLIB_UIPArray_GetNumberOfDatas(sorted);
                    j = 0;
                    for(i = 0; i < i_max; i++){
                        printf("word%3d:%3d:%s\n", i, CHNLIB_UIPArray_GetData32ByIndex(sorted, i), CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(sorted, i)));
                        j += CHNLIB_UIPArray_GetData32ByIndex(sorted, i);
                    }
                    if(i > 0){
                        printf("Average:%d\n", j / i);
                    }
                    //CHNLIB_UIPArray_FreeOnlyArray(&sorted);
                    release(sorted);
                } else if(CHNLIB_String_CompareStringWithCString(temp, "memlist")){
                    CHNLIB_Debug_PrintStructureData(WorkingSet.RootMemory, 0);
                } else if(CHNLIB_String_CompareStringWithCString(temp, "readfile")){
                    //::readfile:filename
                    if(readfp == NULL){
                        readfp = fopen(CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(separated, 4)), "rb");
                        if(readfp == NULL){
                            puts("File open error.");
                        }
                    }
                } else if(CHNLIB_String_CompareStringWithCString(temp, "savewordlist")){
                    AI_System_SaveMemory_RootWordList(CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(separated, 4)));
               } else if(CHNLIB_String_CompareStringWithCString(temp, "savememlist")){
                   AI_System_SaveMemory_RootMemory(CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(separated, 4)));
                } else if(CHNLIB_String_CompareStringWithCString(temp, "addmemtag")){
                    //[not implemented]
                } else{
                    passthink = False;
                }
            }
            
            release(separated);
        }
        
        if(!passthink){
            //AIへの入力
            //::readfile:AITestData_ja.txt
            AI_Think_LearnWordFromInputString(input);
            
            //入力履歴を追加
            i = CHNLIB_UIPArray_AppendLast(&WorkingSet.InputHistory, 0, input);
            if(i > AI_INPUT_HISTORY_MAX){
                //履歴の整理
                for(i = 0; i < AI_INPUT_HISTORY_MAX; i++){
                    CHNLIB_UIPArray_RemoveByIndex(&WorkingSet.InputHistory, 0);
                }
            }
        }
        
        release(input);
    }
    
    return 0;
}

void AI_Think_LearnWordFromInputString(CHNLIB_String *input)
{
    //入力文字列から単語を抽出して記憶する。
    CHNLIB_UIPArray *candidateWordList;
    int i, i_max;
    int tagIndex;
    
    candidateWordList = CHNLIB_UIPArray_Initialize();
    candidateWordList = AI_Think_SlideLookUpWordByHistory(input);
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(candidateWordList);

    printf("Index(Decimal),CountOfContain(Decimal), String\n");
    for(i = 0; i < i_max; i++){
        printf("%d,%d,%s\n", i, CHNLIB_UIPArray_GetData32ByIndex(candidateWordList, i), CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(candidateWordList, i)));
        
        //単語リストに登録。
        tagIndex = AI_Memory_AddRootWordData(CHNLIB_UIPArray_GetPointerByIndex(candidateWordList, i));
        
        CHNLIB_UIPArray_SetData32ByIndex(WorkingSet.RootWordList, tagIndex, CHNLIB_UIPArray_GetData32ByIndex(WorkingSet.RootWordList, tagIndex) + CHNLIB_UIPArray_GetData32ByIndex(candidateWordList, i));
    }
    //すでに存在したタグは追加されていないため、ReferenceCountが1のはずなので、解放される。
    //追加されたタグはReferenceCountが2のため、解放されない。
    release(candidateWordList);
    //CHNLIB_UIPArray_FreeSelectedAll(&candidateWordList);

    return;
}

CHNLIB_UIPArray *AI_Think_SlideLookUpWordByHistory(CHNLIB_String *input)
{
    //[UTF-8]
    //入力文字列と履歴文字列を照らし合わせ、単語の候補を抜き出す。
    //候補単語は、新たに確保されたUIPArrayに格納され、そのArrayへのポインタを返す。
    //data32=入力文字列中に含まれる候補単語の数。
    //pointer=候補単語を示すCHNLIB_String.
    CHNLIB_UIPArray *candidatewordlist;
    int i, i_max;
    int j, j_max;
    int k, k_max;
    const char *cstr_input, *cstrp_input;
    const char *cstr_history, *cstrp_history;
    int candidatelength, templength;
    int cstrp_input_length;
    
    candidatewordlist = CHNLIB_UIPArray_Initialize();
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(WorkingSet.InputHistory);
    
    cstr_input = CHNLIB_String_GetReferencePointerOfCString(input);
    k_max = CHNLIB_UTF8_GetStringLengthByCharacter(cstr_input);
    cstrp_input = cstr_input;
    
    for(k = 0; k < k_max; k++){
        //input character loop
        candidatelength = 0;
        cstrp_input_length = CHNLIB_UTF8_GetStringLengthByCharacter(cstrp_input);
        for(i = 0; i < i_max; i++){
            //history entry loop
            cstr_history = CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(WorkingSet.InputHistory, i));
            j_max = CHNLIB_UTF8_GetStringLengthByCharacter(cstr_history);
            cstrp_history = cstr_history;
            
            for(j = 0; j < j_max; j++){
                //history character loop
                templength = CHNLIB_UTF8_CompareString_LeftHand(cstrp_history, cstrp_input);
                if(templength > candidatelength && templength != cstrp_input_length){
                    //前方一致の長さが、これまで見つかった単語よりも長く、かつ入力文字列の検索部分の全長ではない場合、
                    //単語の候補とする。
                    candidatelength = templength;
                }
                CHNLIB_UTF8_GetNextUnicodeOfCharacter(cstrp_history, &cstrp_history);
            }
        }
        if(candidatelength > 0){
            CHNLIB_UIPArray_AppendLast_ProtectFromDuplication(&candidatewordlist, 0, autorelease(CHNLIB_String_ExtractByLength(input, CHNLIB_UTF8_GetByteSizeFromLengthByCharacter(cstr_input, 0, k - 1), CHNLIB_UTF8_GetByteSizeFromLengthByCharacter(cstrp_input, 0, candidatelength - 1))), &AI_Memory_AddRootWordData_IsDuplicated);
        }
        CHNLIB_UTF8_GetNextUnicodeOfCharacter(cstrp_input, &cstrp_input);
    }
    
    //各候補単語が履歴文字列にいくつ含まれているかをdata32に保存する。
    k_max = CHNLIB_UIPArray_GetNumberOfDatas(candidatewordlist);
    for(k = 0; k < k_max; k++){
        j = 0;
        cstr_input = CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(candidatewordlist, k));
        for(i = 0; i < i_max; i++){
            cstr_history = CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(WorkingSet.InputHistory, i));
            j += CHNLIB_UTF8_GetCountOfContain(cstr_history, cstr_input);
            
        }
        CHNLIB_UIPArray_SetData32ByIndex(candidatewordlist, k, j);
    }
    
    //重複抽出フィルタリング
    AI_Think_CandidateWordList_Filter02(&candidatewordlist, 1);
    AI_Think_CandidateWordList_Filter01(&candidatewordlist, 1);
    AI_Think_CandidateWordList_Filter00(&candidatewordlist);
    
    return candidatewordlist;
}

CHNLIB_UIPArray *AI_Think_WordList_GetSortedByLength(const CHNLIB_UIPArray *wordlist)
{
    CHNLIB_UIPArray *temp, *sorted;
    
    temp = CHNLIB_UIPArray_CopyArray(wordlist);
    
    AI_Think_WordList_SetLengthToData32(&temp);
    sorted = CHNLIB_UIPArray_GetSortedInDescendingOrderByData32(temp);
    
    release(temp);
    
    return sorted;
}

void AI_Think_WordList_SetLengthToData32(CHNLIB_UIPArray **wordlist)
{
    int i, i_max;
    
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(*wordlist);
    
    for(i = 0; i < i_max; i++){
        CHNLIB_UIPArray_SetData32ByIndex(*wordlist, i, CHNLIB_UTF8_GetStringLengthByCharacter(CHNLIB_String_GetReferencePointerOfCString(CHNLIB_UIPArray_GetPointerByIndex(*wordlist, i))));
    }
    
    return;
}

void AI_System_InitializeSystemWorkingSet(void)
{
    //WorkingSetを初期化する。
    int i, i_max;
    
    WorkingSet.SystemWordList0 = CHNLIB_UIPArray_Initialize();
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList0, AI_SW_Delimiter_Colon, autorelease(CHNLIB_String_Initialize(":")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList0, AI_SW_Delimiter_Comma, autorelease(CHNLIB_String_Initialize(",")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList0, AI_SW_Delimiter_Plus, autorelease(CHNLIB_String_Initialize("+")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList0, AI_SW_Bracket_Start, autorelease(CHNLIB_String_Initialize("[")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList0, AI_SW_Bracket_End, autorelease(CHNLIB_String_Initialize("]")));
    
    WorkingSet.SystemWordList1 = CHNLIB_UIPArray_Initialize();
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList1, True, autorelease(CHNLIB_String_Initialize("未定義")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList1, True, autorelease(CHNLIB_String_Initialize("真")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList1, True, autorelease(CHNLIB_String_Initialize("偽")));
    CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList1, True, autorelease(CHNLIB_String_Initialize("あいさつ")));
    
    WorkingSet.SystemWordList2 = CHNLIB_UIPArray_Initialize();
    for(i = 0; i < 10; i++){
        CHNLIB_UIPArray_AppendLast(&WorkingSet.SystemWordList2, True, autorelease(CHNLIB_String_InitializeWithFormat("%d", i)));
    }
    
    WorkingSet.RootWordList = CHNLIB_UIPArray_Initialize();
    WorkingSet.RootMemory = CHNLIB_UIPArray_Initialize();
    WorkingSet.InputHistory = CHNLIB_UIPArray_Initialize();
    
    //WordListへシステム文字列を追加
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(WorkingSet.SystemWordList0);
    for(i = 0; i < i_max; i++){
        CHNLIB_UIPArray_AppendLast(&WorkingSet.RootWordList, 0, CHNLIB_UIPArray_GetPointerByIndex(WorkingSet.SystemWordList0, i));
    }
    
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(WorkingSet.SystemWordList1);
    for(i = 0; i < i_max; i++){
        CHNLIB_UIPArray_AppendLast(&WorkingSet.RootWordList, 0, CHNLIB_UIPArray_GetPointerByIndex(WorkingSet.SystemWordList1, i));
    }
    
    i_max = CHNLIB_UIPArray_GetNumberOfDatas(WorkingSet.SystemWordList2);
    for(i = 0; i < i_max; i++){
        CHNLIB_UIPArray_AppendLast(&WorkingSet.RootWordList, 0, CHNLIB_UIPArray_GetPointerByIndex(WorkingSet.SystemWordList2, i));
    }
    
    return;
}
