//#include "StdAfx.h"
#include <windows.h>
#include <stdio.h>
#pragma hdrstop

#include "kbini.h"
#include "kbstr.h"

static BOOL __fastcall IsSection(char *pszLine, char **ppszSection);
static BOOL __fastcall IsKeyValue(char *pszLine,
                                  char **ppszKey,
                                  char **ppszValue);

static BOOL __fastcall IsSection(char *pszLine, char **ppszSection)
{//pszLine が
 //[SectionName]
 //形式の行なら ppszSection にセクション名を入れて TRUE を返す
    if(*pszLine != '['){
        *ppszSection = NULL;
        return FALSE;
    }
    *ppszSection = ++pszLine;
    while(*pszLine){
        if(kbIsLeadByte(*pszLine)){
            pszLine += 2;
        }
        else{
            if(*pszLine == ']' && !*(pszLine+1)){
                *pszLine = 0;
                kbStrTrim(*ppszSection);
                return TRUE;
            }
            pszLine++;
        }
    }
    *ppszSection = NULL;
    return FALSE;
}
static BOOL __fastcall IsKeyValue(char *pszLine,
                                  char **ppszKey,
                                  char **ppszValue)
{//pszLine が
 //KeyName=Value
 //形式の行なら *ppszKey にキー名、*ppszValue に値を入れて TRUE を返す
    //コメントかチェック
    if(pszLine[0] == ';'){
        pszLine[0] = 0;
    }
    *ppszKey = kbStrTok(pszLine, "=", ppszValue);
    if(*ppszKey && *ppszValue){
        kbStrTrimRight(*ppszKey);
        if(**ppszKey){
            kbStrTrimLeft(*ppszValue);
            return TRUE;
        }
    }
    *ppszKey = *ppszValue = NULL;
    return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
KbIniKeyValue::KbIniKeyValue(const char *cszKeyName, const char *cszValue)
{//Key と Value はメモリを共有する
    const int nKeyLen = strlen(cszKeyName) + 1;
    const int nValLen = strlen(cszValue) + 1;
    m_pszKey = (char*)malloc(nKeyLen + nValLen);
    m_pszValue = m_pszKey + nKeyLen;
    memcpy(m_pszKey, cszKeyName, nKeyLen);
    memcpy(m_pszValue, cszValue, nValLen);
}
KbIniKeyValue::~KbIniKeyValue(void)
{
    free(m_pszKey);
    //free(m_pszValue);//m_pszKey と共有しているので不要
}
void __fastcall KbIniKeyValue::SetValue(const char *cszValue)
{
    const int nOldValLen = strlen(m_pszValue) + 1;
    const int nNewValLen = strlen(cszValue) + 1;
    if(nNewValLen > nOldValLen){
        const int nKeyLen = m_pszValue - m_pszKey;
        m_pszKey = (char*)realloc(m_pszKey, nKeyLen + nNewValLen);
        m_pszValue = m_pszKey + nKeyLen;
    }
    memcpy(m_pszValue, cszValue, nNewValLen);
}
KbIniSection::KbIniSection(const char *cszSectionName)
{
    m_pszSection = strdup(cszSectionName);
    m_ppKeyValues = NULL;
    m_nCapacity = m_nCount = 0;
}
KbIniSection::~KbIniSection(void)
{
    free(m_pszSection);
    const int nCount = m_nCount;
    int i;
    for(i = 0; i < nCount; i++){
        delete m_ppKeyValues[i];
    }
    if(m_ppKeyValues){
        free(m_ppKeyValues);
    }
}
KbIniKeyValue* __fastcall KbIniSection::FindKeyValue(const char *cszKey)
{
    const int nCount = m_nCount;
    int i;
    for(i = 0; i < nCount; i++){
        if(kbStrCmpI(m_ppKeyValues[i]->GetKeyName(), cszKey) == 0){
            return m_ppKeyValues[i];
        }
    }
    return NULL;
}
KbIniKeyValue* __fastcall KbIniSection::GetKeyValue(int nIndex)
{
    if(nIndex < 0 || nIndex >= m_nCount){
        return NULL;
    }
    return m_ppKeyValues[nIndex];
}
void __fastcall KbIniSection::AddKeyValue(const char *cszKey, const char *cszValue)
{
    KbIniKeyValue *pKeyValue = FindKeyValue(cszKey);
    if(pKeyValue){
        pKeyValue->SetValue(cszValue);
        return;
    }
    if(m_nCount >= m_nCapacity){
        if(!m_nCapacity){
            m_nCapacity = 16;
        }
        else{
            m_nCapacity *= 2;
        }
        m_ppKeyValues = (KbIniKeyValue**)realloc(m_ppKeyValues, sizeof(KbIniKeyValue*)*m_nCapacity);
    }
    pKeyValue = new KbIniKeyValue(cszKey, cszValue);
    m_ppKeyValues[m_nCount++] = pKeyValue;
}
KbIniFile::KbIniFile(void)
{
    m_szFileName[0] = 0;
    m_ppSections = NULL;
    m_nCount = m_nCapacity = 0;
}
KbIniFile::~KbIniFile(void)
{
    Close();
}
KbIniSection* __fastcall KbIniFile::GetIniSection(int nIndex)
{
    if(nIndex < 0 || nIndex >= m_nCount){
        return NULL;
    }
    return m_ppSections[nIndex];
}
int __fastcall KbIniFile::ReadInt(const char *cszSection,
                                  const char *cszKey,
                                  int nDefault)
{
    KbIniSection *pSection = FindSection(cszSection, FALSE);
    int nRet = nDefault;
    if(pSection){
        KbIniKeyValue *pKeyValue = pSection->FindKeyValue(cszKey);
        if(pKeyValue){
            nRet = kbStrToIntDef(pKeyValue->GetValue(), nDefault);
        }
    }
    return nRet;
}
char* __fastcall KbIniFile::ReadStr(const char *cszSection,
                                    const char *cszKey,
                                    const char *cszDefault,
                                    char *szString,
                                    int  nSize)
{
    KbIniSection *pSection = FindSection(cszSection, FALSE);
    if(pSection){
        KbIniKeyValue *pKeyValue = pSection->FindKeyValue(cszKey);
        if(pKeyValue){
            kbStrLCpy(szString, pKeyValue->GetValue(), nSize);
            return szString;
        }
    }
    kbStrLCpy(szString, cszDefault, nSize);
    return szString;
}
void __fastcall KbIniFile::WriteInt(const char *cszSection,
                                    const char *cszKey,
                                    int nValue)
{
    KbIniSection *pSection = FindSection(cszSection, TRUE);
    char szValue[64];
    pSection->AddKeyValue(cszKey, itoa(nValue, szValue, 10));
}
void __fastcall KbIniFile::WriteStr(const char *cszSectionName,
                                    const char *cszKey,
                                    const char *cszValue)
{
    KbIniSection *pSection = FindSection(cszSectionName, TRUE);
    pSection->AddKeyValue(cszKey, cszValue);
}
void __fastcall KbIniFile::Flush(void)
{
    if(!m_szFileName[0]){
        return;
    }
    FILE *fp = fopen(m_szFileName, "wt");
    if(!fp){
        return;
    }
    int i, j;
    const int nCount = m_nCount;
    for(i = 0; i < nCount; i++){
        KbIniSection *pSection = m_ppSections[i];
        fputs("[", fp);
        fputs(pSection->GetSectionName(), fp);
        fputs("]\n", fp);
        j = 0;
        while(1){
            KbIniKeyValue *pKeyValue = pSection->GetKeyValue(j++);
            if(!pKeyValue){
                break;
            }
            fputs(pKeyValue->GetKeyName(), fp);
            fputs("=", fp);
            fputs(pKeyValue->GetValue(), fp);
            fputs("\n", fp);
        }
    }
    fclose(fp);
}
KbIniSection* __fastcall KbIniFile::FindSection(const char *cszSection, BOOL bCreate)
{//セクションを検索
 //見つからない場合は、bCreate が TRUE なら作る
    const int nCount = m_nCount;
    int i;
    for(i = 0; i < nCount; i++){
        if(kbStrCmpI(m_ppSections[i]->GetSectionName(), cszSection) == 0){
            return m_ppSections[i];
        }
    }
    if(!bCreate){
        return NULL;
    }
    if(m_nCount >= m_nCapacity){
        if(!m_nCapacity){
            m_nCapacity = 16;
        }
        else{
            m_nCapacity *= 2;
        }
        m_ppSections = (KbIniSection**)realloc(m_ppSections, sizeof(KbIniSection*)*m_nCapacity);
    }
    KbIniSection *pNewSection = new KbIniSection(cszSection);
    m_ppSections[m_nCount++] = pNewSection;
    return pNewSection;
}
BOOL __fastcall KbIniFile::Open(const char *cszFileName)
{
    Close();
    HANDLE hFile = CreateFile(cszFileName,
                              GENERIC_READ,
                              FILE_SHARE_READ|FILE_SHARE_WRITE,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);
    if(hFile != INVALID_HANDLE_VALUE){
        DWORD dwSizeHigh = 0;
        DWORD dwSize = GetFileSize(hFile, &dwSizeHigh);
        if(dwSizeHigh){
            CloseHandle(hFile);
            return FALSE;
        }
        const DWORD cdwMaxStack = 32*1024;
        if(dwSize < cdwMaxStack){//32KB 以下ならスタックで処理
            BYTE buf[cdwMaxStack+1];
            ReadFile(hFile, buf, dwSize, &dwSize, NULL);
            buf[dwSize] = 0;
            OpenFromBuffer(buf, dwSize);
        }
        else{
            if(dwSize > cdwMaxStack*4){
                dwSize = cdwMaxStack*4;
            }
            BYTE *buf = (BYTE*)malloc(dwSize+1);
            ReadFile(hFile, buf, dwSize, &dwSize, NULL);
            buf[dwSize] = 0;
            OpenFromBuffer(buf, dwSize);
            free(buf);
        }
        CloseHandle(hFile);
    }
    kbStrLCpy(m_szFileName, cszFileName, sizeof(m_szFileName));
    return TRUE;
}
BOOL __fastcall KbIniFile::OpenFromBuffer(BYTE *pBuffer, DWORD dwSize)
{
    Close();
    KbIniSection *pCurrentSection = NULL;
    char *pszLine = (char*)pBuffer;
    char *pszNextLine;
    char *pszSection = "";
    char *pszNextSection;
    char *pszKey;
    char *pszValue;
    while(kbStrTok(pszLine, "\r\n", &pszNextLine)){
        if(!*pszLine){
            pszLine = pszNextLine;
            continue;
        }
        kbStrTrim(kbCRLFtoSpace(pszLine));
        if(IsSection(pszLine, &pszNextSection)){
            pCurrentSection = NULL;
            pszSection = pszNextSection;
        }
        else if(*pszSection &&
                IsKeyValue(pszLine, &pszKey, &pszValue)){
            if(!pCurrentSection){
                pCurrentSection = FindSection(pszSection, TRUE);
            }
            pCurrentSection->AddKeyValue(pszKey, pszValue);
        }
        pszLine = pszNextLine;
    }
    return TRUE;
}
void __fastcall KbIniFile::Close(void)
{
    m_szFileName[0] = 0;
    const int nCount = m_nCount;
    if(nCount){
        int i;
        for(i = 0; i < nCount; i++){
            delete m_ppSections[i];
        }
        free(m_ppSections);
        m_ppSections = NULL;
        m_nCount = m_nCapacity = 0;
    }
}

