/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation
 *
 * Portions created by the Initial Developer are Copyright (C) 2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *       Rajiv Dayal <rdayal@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <sys/stat.h>
#include <TCHAR.H>
#include <malloc.h>
#include <syncmgr.h>
#include <Pgenerr.h>

#include "MozABHHManager.h"

MozABHHManager::MozABHHManager(DWORD dwGenericFlags, 
               char *pName, 
               DWORD dwCreator, 
               DWORD dwType, 
               WORD dbFlags, 
               WORD dbVersion, 
			   int  iCardNum,
               eSyncTypes syncType)
{
    m_hhDB = 0;
    
    m_rInfo.m_pBytes = NULL;
    m_rInfo.m_TotalBytes = 0;

    m_dwMaxRecordCount = 0;
    m_pCatMgr = NULL;
    // since we are dealing with AB which is a Record DB in Palm
    m_bRecordDB = TRUE;

    m_szName[0] = '\0';
    SetName( pName);
    m_dwCreator = dwCreator;
    m_dwType    = dwType;
    m_wFlags    = dbFlags;
    m_wVersion  = dbVersion;
    m_CardNum = iCardNum;

    memset(&m_appInfo, 0, sizeof(CDbGenInfo));
}

MozABHHManager::~MozABHHManager()
{
    if (m_rInfo.m_pBytes != NULL) {
        delete m_rInfo.m_pBytes;
    }
    m_rInfo.m_pBytes = NULL;
    m_rInfo.m_TotalBytes = 0;

    if (m_appInfo.m_pBytes) {
        delete m_appInfo.m_pBytes;
        m_appInfo.m_pBytes = NULL;
    }

    if (m_pCatMgr) {
        delete m_pCatMgr;
        m_pCatMgr = NULL;
    }

    CloseDB(TRUE);
}


long MozABHHManager::AllocateDBInfo(CDbGenInfo &info, BOOL bClearData)
{
    BOOL bNew = FALSE;

    if (!info.m_pBytes) {
        info.m_pBytes = (BYTE *) new char[MAX_RECORD_SIZE];  // deleted in d'ctor or by caller
        if (!info.m_pBytes)
            return GEN_ERR_LOW_MEMORY;
        info.m_TotalBytes = MAX_RECORD_SIZE;
        bNew = TRUE;
    } 

    if ((bClearData) || (bNew)) {
        memset(info.m_pBytes, 0, info.m_TotalBytes);
    }
    return 0;
}

long MozABHHManager::GetInfoBlock(CDbGenInfo &info, BOOL bSortInfo)
{
    long  retval=0;

    // Allocate storage for app/sort info blocks
    retval = AllocateDBInfo(info);
    if (retval)
        return retval;

    _tcsncpy((char*)info.m_FileName, m_szName, sizeof(info.m_FileName)-1);
    info.m_FileName[sizeof(info.m_FileName)-1] = '\0';
    memset(info.m_pBytes, 0, info.m_TotalBytes);

    if (!bSortInfo) {
        // Read the AppInfo block
        retval = SyncReadDBAppInfoBlock(m_hhDB, info);
    } else {
        // Read the SortInfo block
        retval = SyncReadDBSortInfoBlock(m_hhDB, info);
    }
    if (retval) {// if error then clean up
        delete info.m_pBytes;
        info.m_pBytes = NULL;
        info.m_TotalBytes = 0;
    }

    return retval;
}


long MozABHHManager::GetAppInfo(CDbGenInfo &rInfo)
{
    long retval = 0;
    BYTE *pBuffer;

    pBuffer = rInfo.m_pBytes; 
    WORD wTotalBytes = rInfo.m_TotalBytes;
    memset(&rInfo, 0, sizeof(CDbGenInfo));
    if ((wTotalBytes > 0) && (pBuffer)) {
        memset(pBuffer, 0, wTotalBytes);
        rInfo.m_pBytes       = pBuffer; 
        rInfo.m_TotalBytes   = wTotalBytes;
    }

    retval = GetInfoBlock(rInfo);

    if (retval) { // if error cleanup
        if (rInfo.m_pBytes) {
            delete rInfo.m_pBytes;
            rInfo.m_pBytes = NULL;
            rInfo.m_TotalBytes   = 0;
        }
    }

    return retval;
}

long MozABHHManager::SetName( char *pName)
{
    if (m_hhDB)
        return GEN_ERR_DB_ALREADY_OPEN;

    WORD wLen;
    if (!pName){
        *m_szName = '\0';
        return 0;
    } 
    wLen = (WORD)strlen(pName);
    if (!wLen) {
        *m_szName = '\0';
        return 0;
    }
    if (wLen >= sizeof(m_szName))
        return GEN_ERR_DATA_TOO_LARGE;

    strncpy(m_szName, pName, sizeof(m_szName)-1);
    m_szName[sizeof(m_szName)-1] = '\0';
    return 0;
}

long MozABHHManager::LoadCategories(void)
{
    long retval=0;

    if (m_pCatMgr) {  // remove existing manager
        delete m_pCatMgr;
        m_pCatMgr = NULL;
    }

    if (m_appInfo.m_BytesRead == 0){
        retval = GetAppInfo(m_appInfo);
        if (retval)
            return retval;
    }

    if ((m_appInfo.m_BytesRead > 0) && (m_appInfo.m_pBytes)){
        m_pCatMgr = new CPCategoryMgr(m_appInfo.m_pBytes, m_appInfo.m_BytesRead); // deleted in d'ctor
        if (!m_pCatMgr)
            return GEN_ERR_LOW_MEMORY;
    } else {
        retval = GEN_ERR_NO_CATEGORIES;
    }
    return retval;
}

long MozABHHManager::AddCategory(CPCategory & cat)
{
    long retval=0;

    if(!m_pCatMgr)
        retval = LoadCategories();

    // generate a new ID and Add the category
    if(!retval) {
        int id;
        for(id = 0; id <= MAX_CATEGORIES; id++)
            if(!m_pCatMgr->FindID(id))
                break;
        // if ID doesnot already exist
        if(id < MAX_CATEGORIES) {
            cat.SetID(MAKELONG(id, 0));
            retval = m_pCatMgr->Add(cat);
        }
        else
            retval = CAT_NO_ID;
    }

    if(!retval)
        m_pCatMgr->SetChanged();

    return retval;    
}

long MozABHHManager::DeleteCategory(DWORD dwCategory, BOOL bMoveToUnfiled)
{
    long retval=0;

    if ((dwCategory < 0) || (dwCategory >= MAX_CATEGORIES))
        return CAT_ERR_INDEX_OUT_OF_RANGE;

    BYTE sCategory = LOBYTE(LOWORD(dwCategory));

    if (!bMoveToUnfiled)
    {
        retval = SyncPurgeAllRecsInCategory(m_hhDB, sCategory);
        m_pCatMgr->DeleteByIndex(dwCategory); // delete category itself
    }
    else 
        retval = SyncChangeCategory(m_hhDB, sCategory, 0);
    return retval;
}

long MozABHHManager::ChangeCategory(DWORD dwOldCatIndex, DWORD dwNewCatIndex)
{
    long retval=0;

    if ((dwOldCatIndex < 0) || (dwOldCatIndex >= MAX_CATEGORIES))
        return CAT_ERR_INDEX_OUT_OF_RANGE;

    BYTE sCategory = LOBYTE(LOWORD(dwOldCatIndex));

    if ((dwNewCatIndex < 0) || (dwNewCatIndex >= MAX_CATEGORIES))
        return CAT_ERR_INDEX_OUT_OF_RANGE;

    BYTE sNewCategory = LOBYTE(LOWORD(dwNewCatIndex));

    retval = SyncChangeCategory(m_hhDB, sCategory, sNewCategory);
    return retval;
}

long MozABHHManager::CompactCategoriesToHH()
{
    long retval = 0;

    if ((!m_pCatMgr) || (!m_pCatMgr->IsChanged()))
        return 0;

    retval = AllocateDBInfo(m_appInfo);
    if (retval)
        return retval;

    DWORD dwRecSize;

    dwRecSize = m_appInfo.m_TotalBytes;

    retval = m_pCatMgr->Compact(m_appInfo.m_pBytes, &dwRecSize);
    if (!retval) {
        // more than just the categories may be stored in the app info structure
        // This code only replaces the category area.
        if (!m_appInfo.m_BytesRead)
            m_appInfo.m_BytesRead = LOWORD(dwRecSize);
    }

    if(!retval)
        retval = SyncWriteDBAppInfoBlock(m_hhDB, m_appInfo);

    return retval;
}

long MozABHHManager::OpenDB(BOOL bCreate)
{
    long retval=0;

    if (bCreate) {
       CDbCreateDB  createInfo;

        // Delete any existing DB with the same name
        SyncDeleteDB(m_szName, m_CardNum);

        memset(&createInfo, 0, sizeof(CDbCreateDB));

        createInfo.m_Creator     = m_dwCreator; 
        createInfo.m_Type        = m_dwType; 
        createInfo.m_Flags       = (eDbFlags) m_wFlags;
        createInfo.m_CardNo     = m_CardNum;  
        strncpy(createInfo.m_Name, m_szName, sizeof(createInfo.m_Name)-1);
        createInfo.m_Name[sizeof(createInfo.m_Name)-1] = '\0';
        createInfo.m_Version    = m_wVersion;

        if ((retval = SyncCreateDB(createInfo)) == SYNCERR_NONE)
            m_hhDB = createInfo.m_FileHandle;
    }
    else 
    {
        retval = SyncOpenDB(m_szName, 
                            m_CardNum, 
                            m_hhDB,
                            eDbRead | eDbWrite);
        if ((retval) && (retval == SYNCERR_NOT_FOUND))
            retval = GEN_ERR_NO_HH_DB;
    } 

    return retval;
}

long MozABHHManager::CloseDB(BOOL bDontUpdate)
{
    long retval = 0;
    long retval2 = 0;

    if (m_hhDB == NULL) 
        return GEN_ERR_DB_NOT_OPEN;

    if (!bDontUpdate) {
        retval = SyncResetSyncFlags(m_hhDB);
    }
    retval2 = SyncCloseDB(m_hhDB);
    if (!retval)
        retval = retval2;
    m_hhDB = NULL;
    return retval;
}

long MozABHHManager::LoadAllUpdatedRecords(CPalmRecord ***ppRecordList, DWORD * pListSize)
{
    return LoadUpdatedRecords(0, ppRecordList, pListSize);
}

long MozABHHManager::LoadUpdatedRecordsInCategory(DWORD catIndex, CPalmRecord ***ppRecordList, DWORD * pListSize)
{
    return LoadUpdatedRecords(catIndex, ppRecordList, pListSize);
}

// this function allocates the list as well as the records, caller should free list and delete records
long MozABHHManager::LoadUpdatedRecords(DWORD catIndex, CPalmRecord ***ppRecordList, DWORD * pListSize)
{
    long retval=0;
    WORD dwRecCount=0;

    retval = SyncGetDBRecordCount(m_hhDB, dwRecCount);
    if (retval)
        return retval;

    // resset the iteration index
    retval = SyncResetRecordIndex(m_hhDB);
    if (retval)
        return retval;

    // allocate buffer initially for the total record size and adjust after getting records
    CPalmRecord ** palmRecordList = (CPalmRecord **) malloc(sizeof(CPalmRecord *) * dwRecCount);
    if (!palmRecordList)
        return GEN_ERR_LOW_MEMORY;
    memset(palmRecordList, 0, sizeof(CPalmRecord *) * dwRecCount);
    *ppRecordList = palmRecordList;

    // SyncReadNextModifiedRecInCategory() does not seem to be returning
    // deleted palm records, so SyncReadNextModifiedRec() is used instead.
    CPalmRecord *pPalmRec;
    *pListSize = 0;
    while ((!retval) && (*pListSize < dwRecCount))  {
        retval = AllocateRawRecord();
        if(retval)
            break;
        m_rInfo.m_RecIndex = 0;
        retval = SyncReadNextModifiedRec(m_rInfo);
        // Does it belong to the category we care about?
        if (!retval && m_rInfo.m_CatId == catIndex)
        {
            pPalmRec = new CPalmRecord(m_rInfo);
            if (pPalmRec) {
                *palmRecordList = pPalmRec;
                palmRecordList++;
                (*pListSize)++;
            } else {
                retval = GEN_ERR_LOW_MEMORY;
            }
        } 
    }

    // reallocate to the correct size
    if((*pListSize) != dwRecCount)
        *ppRecordList=(CPalmRecord **) realloc(*ppRecordList, sizeof(CPalmRecord *) * (*pListSize));

    if (retval == SYNCERR_FILE_NOT_FOUND) // if there are no more records
        retval = 0;

    return retval;
}

long MozABHHManager::AllocateRawRecord(void)
{
    if (m_rInfo.m_pBytes != NULL) {
        BYTE *pBytes;

        pBytes = m_rInfo.m_pBytes;
        memset(&m_rInfo, 0, sizeof(CRawRecordInfo));
        memset(pBytes, 0, MAX_RECORD_SIZE);

        m_rInfo.m_pBytes        = pBytes;
        m_rInfo.m_TotalBytes    = MAX_RECORD_SIZE;
        m_rInfo.m_FileHandle    = m_hhDB;
        return 0;
    }
    
    memset(&m_rInfo, 0, sizeof(CRawRecordInfo));
    m_rInfo.m_pBytes        = (BYTE *) new char[MAX_RECORD_SIZE];
    if (!m_rInfo.m_pBytes)
        return GEN_ERR_LOW_MEMORY;

    memset(m_rInfo.m_pBytes, 0, MAX_RECORD_SIZE);
    m_rInfo.m_TotalBytes    = MAX_RECORD_SIZE;
    m_rInfo.m_FileHandle    = m_hhDB;
    return 0;
}

// this function allocates the list as well as the records, caller should free list and delete records
long MozABHHManager::LoadAllRecords(CPalmRecord ***ppRecordList, DWORD * pListSize)
{
    long retval=0;
    WORD dwRecCount, dwIndex;

    retval = SyncGetDBRecordCount(m_hhDB, dwRecCount);
    if (!retval)
        return retval;
    *pListSize = dwRecCount;

    *ppRecordList = (CPalmRecord **) malloc(sizeof(CPalmRecord *) * dwRecCount);
    if (!*ppRecordList)
        return GEN_ERR_LOW_MEMORY;
    memset(*ppRecordList, 0, sizeof(CPalmRecord *) * dwRecCount);

    CPalmRecord *pPalmRec;
    for (dwIndex = 0; (dwIndex < dwRecCount) && (!retval); dwIndex++){
        retval = AllocateRawRecord();
        if(retval)
            break;

        m_rInfo.m_RecIndex = LOWORD(dwIndex);

        retval = SyncReadRecordByIndex(m_rInfo);
        if (!retval) {
            pPalmRec = new CPalmRecord(m_rInfo);
            if (pPalmRec) {
                *ppRecordList[dwIndex] = pPalmRec;
            } else {
                retval = GEN_ERR_LOW_MEMORY;
            }
        } else if (retval != SYNCERR_FILE_NOT_FOUND) {
            pPalmRec = new CPalmRecord(m_rInfo);
            pPalmRec->Initialize();
            pPalmRec->SetIndex(dwIndex);
            *ppRecordList[dwIndex] = pPalmRec;
        }
    }
    m_dwMaxRecordCount = dwIndex + 1;

    if (retval == SYNCERR_FILE_NOT_FOUND) // if there are no more records
        retval = 0;

    return retval;
}

// this function allocates the list as well as the records, caller should free list and delete records
long MozABHHManager::LoadAllRecordsInCategory(DWORD catIndex, CPalmRecord ***ppRecordList, DWORD * pListSize)
{
    long retval=0;
    WORD dwRecCount=0;

    retval = SyncGetDBRecordCount(m_hhDB, dwRecCount);
    if (retval)
        return retval;

    // resset the iteration index
    retval = SyncResetRecordIndex(m_hhDB);
    if (retval)
        return retval;

    // allocate buffer initially for the total record size and adjust after getting records
    CPalmRecord ** palmRecordList = (CPalmRecord **) malloc(sizeof(CPalmRecord *) * dwRecCount);
    if (!palmRecordList)
        return GEN_ERR_LOW_MEMORY;
    memset(palmRecordList, 0, sizeof(CPalmRecord *) * dwRecCount);
    *ppRecordList = palmRecordList;

    CPalmRecord *pPalmRec;

    *pListSize = 0;
    while ((!retval) && (*pListSize < dwRecCount))  {
        retval = AllocateRawRecord();
        if (retval) 
            break;
        m_rInfo.m_RecIndex = 0;
        m_rInfo.m_CatId = catIndex;
        retval = SyncReadNextRecInCategory(m_rInfo);
        if (!retval) {
            pPalmRec = new CPalmRecord(m_rInfo);
            if (pPalmRec) {
                *palmRecordList = pPalmRec;
                palmRecordList++;
                (*pListSize)++;
            } else {
                retval = GEN_ERR_LOW_MEMORY;
            }
        } 
    }

    // reallocate to the correct size
    if((*pListSize) != dwRecCount)
        *ppRecordList=(CPalmRecord **) realloc(*ppRecordList, sizeof(CPalmRecord *) * (*pListSize));

    if (retval == SYNCERR_FILE_NOT_FOUND) // if there are no more records
        retval = 0;

    return retval;
}

long MozABHHManager::AddRecords(CPalmRecord **pRecordList, DWORD pCount)
{
    long retval=0;

    for (unsigned long i=0; i < pCount; i++)
    {
        CPalmRecord palmRec = *pRecordList[i];

        palmRec.ResetAttribs();

        retval = AllocateRawRecord();
        if (retval) 
            return retval;

        retval = palmRec.ConvertToHH(m_rInfo);
        if (!retval) {
            m_rInfo.m_FileHandle = m_hhDB;
            m_rInfo.m_RecId = 0;
            if (m_bRecordDB)
                retval = SyncWriteRec(m_rInfo);
            else 
                retval = SyncWriteResourceRec(m_rInfo);
            if (!retval) {
                palmRec.SetIndex(m_rInfo.m_RecIndex);
                palmRec.SetID(m_rInfo.m_RecId);
            }
        }
        else
            return retval;
    }

    return retval;
}

long MozABHHManager::UpdateRecords(CPalmRecord **pRecordList, DWORD pCount)
{
    long retval=0;

    for (unsigned long i=0; i < pCount; i++)
    {
        CPalmRecord palmRec = *pRecordList[i];

        retval = AllocateRawRecord();
        if (retval) 
            return retval;

        retval = palmRec.ConvertToHH(m_rInfo);
        if (!retval) {
            m_rInfo.m_FileHandle = m_hhDB;
            if (m_bRecordDB)
                retval = SyncWriteRec(m_rInfo);
            else 
                retval = SyncWriteResourceRec(m_rInfo);
            if (!retval) {
                palmRec.SetIndex(m_rInfo.m_RecIndex);
                palmRec.SetID(m_rInfo.m_RecId);
            }
        }
        else
            return retval;
    }

    return retval;
}

long MozABHHManager::DeleteRecords(CPalmRecord **pRecordList, DWORD pCount)
{
    long retval=0;

    for (unsigned long i=0; i < pCount; i++)
    {
        CPalmRecord palmRec = *pRecordList[i];

        retval = AllocateRawRecord();
        if (retval) 
            return retval;

        retval = palmRec.ConvertToHH(m_rInfo);
        if (!retval) {
            m_rInfo.m_FileHandle = m_hhDB;
            if (m_bRecordDB)
                retval = SyncDeleteRec(m_rInfo);
            else 
                retval = SyncDeleteResourceRec(m_rInfo);
        }
        else
            return retval;
    }

    return retval;
}


long MozABHHManager::AddARecord(CPalmRecord & palmRec)
{
    long retval=0;

    palmRec.ResetAttribs();

    retval = AllocateRawRecord();
    if (retval) 
        return retval;

    retval = palmRec.ConvertToHH(m_rInfo);
    if (!retval) {
        m_rInfo.m_FileHandle = m_hhDB;
        m_rInfo.m_RecId = 0;
        if (m_bRecordDB)
            retval = SyncWriteRec(m_rInfo);
        else 
            retval = SyncWriteResourceRec(m_rInfo);
        if (!retval) {
            palmRec.SetIndex(m_rInfo.m_RecIndex);
            palmRec.SetID(m_rInfo.m_RecId);
        }
    }

    return retval;
}

long MozABHHManager::UpdateARecord(CPalmRecord & palmRec)
{
    long retval=0;

    retval = AllocateRawRecord();
    if (retval) 
        return retval;

    retval = palmRec.ConvertToHH(m_rInfo);
    if (!retval) {
        m_rInfo.m_FileHandle = m_hhDB;
        m_rInfo.m_Attribs = eRecAttrDirty;
        if (m_bRecordDB)
            retval = SyncWriteRec(m_rInfo);
        else 
            retval = SyncWriteResourceRec(m_rInfo);
        if (!retval) {
            palmRec.SetIndex(m_rInfo.m_RecIndex);
            palmRec.SetID(m_rInfo.m_RecId);
        }
    }

    return retval;
}

long MozABHHManager::DeleteARecord(CPalmRecord & palmRec)
{
    long retval=0;

    retval = AllocateRawRecord();
    if (retval) 
        return retval;

    retval = palmRec.ConvertToHH(m_rInfo);
    if (!retval) {
        m_rInfo.m_FileHandle = m_hhDB;
        if (m_bRecordDB)
            retval = SyncDeleteRec(m_rInfo);
        else 
            retval = SyncDeleteResourceRec(m_rInfo);
    }

    return retval;
}

long MozABHHManager::PurgeDeletedRecs(void)
{
    return (SyncPurgeDeletedRecs(m_hhDB));
}
