/*!
    @file           Tools_Properties.cpp
    @author         MarcW
    @brief          reading, storing properties to/from a file - Implementation

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


\endif
*/

/* 
  -----------------------------------------------------------------------------
  includes
  -----------------------------------------------------------------------------
*/
#include "heo06.h"
#include "ToolsCommon/Tools_Properties.hpp"

Tools_Properties::Tools_Properties()
        : first(NULL),
          filename(NULL) {
    errReason[39] = '\0';
}


Tools_Properties::Tools_Properties( const Tools_DynamicUTF8String& rawfilename )
        : first(NULL),
          filename(NULL) {
    errReason[39] = '\0';
    filename = getCharPointer(rawfilename);
}

Tools_Properties::~Tools_Properties(){
    if( first != NULL )
        delete first;
    if( filename != NULL )
        delete[] filename;
}

SAPDB_Bool Tools_Properties::load() {

    tsp00_Int4 fhandle;
    tsp05_RteFileError ferror;

    // we cannot load if we have no file name
    if( filename == NULL )
        return false;

    // open file
    sqlfopenc(
        filename,
        sp5vf_text,
        sp5vf_read,
        sp5bk_buffered,
        &fhandle,
        &ferror);
    
    // we could not open the file for some reason
    if( ferror.sp5fe_result != vf_ok ) {
        storeReason(ferror.sp5fe_text);
        return false;
    }
    
    char line[8192];
    tsp00_Longint         outLen;
    Tools_Property* p(NULL);

    // read file linewise
    while (ferror.sp5fe_result == vf_ok) {
        sqlfreadc( fhandle, line, sizeof(line), &outLen, &ferror );
        if( ferror.sp5fe_result == vf_ok && line[0]!='#' ) {
            //we could read the line and it is not a comment
            Tools_Property* temp(createProperty(line));
            if( temp != NULL )
                p = insertProperty(p, temp);
        }
    }

    // close the file
    sqlfclosec( fhandle, sp5vf_close_normal, &ferror);
    if( ferror.sp5fe_result != vf_ok ) {
        // some error occurred
        // revert all we've done in loop
        if( p != NULL )
            delete p;
        storeReason(ferror.sp5fe_text);
        return false;
    } else {
        if( first != NULL )
            // delete the properties that we had before loading the new ones
            delete first;
        first = p;
        return true;
    }
}

SAPDB_Bool Tools_Properties::load( const Tools_DynamicUTF8String& rawfilename ) {
    Tools_DynamicUTF8String temp(filename);
    setFileName( rawfilename );
    if( !load() ) {
        // set old file name again
        setFileName( temp );
        return false;
    }
    return true;
}

Tools_Property* Tools_Properties::createProperty( const char* line ) const {
    // points to '=' character
    const char* val = strchr( line, '=' );
    if( val == NULL )
        return NULL;
    // check if there is a kay that makes sense
    if( ((int)(val-line)) <= NULL )
        return NULL;

    val++;
    // now val points behind the '=' character

    // create a NULL-terminated string for the key
    char* key = new char[(int)(val-line)];
    strncpy(key, line, (int)(val-line)-1);

    key[-1 + (int)(val-line)] = 0;
    Tools_Property* p = new Tools_Property( key, val );
    delete[] key;
    return p;
}

SAPDB_Bool Tools_Properties::store() {
    tsp00_Int4 fhandle;
    tsp05_RteFileError ferror;

    // do nothing if we have no file name
    if( filename == NULL )
        return false;

    // try to open the file for writing, overwrite old content
    sqlfopenc(
        filename,
        sp5vf_text,
        sp5vf_write,
        sp5bk_buffered,
        &fhandle,
        &ferror);
    
    // something went wrong opening
    if( ferror.sp5fe_result != vf_ok ) {
        storeReason(ferror.sp5fe_text);
        return false;
    }
    // let's hope that property lines will never be longer
    char line[8192];
    Tools_Property* curr = first;

    // for each property, create a line and write it. Key and value
    // are separated by '=' char.
    while( ferror.sp5fe_result == vf_ok && curr != NULL ) {
        int keylen = strlen(curr->getKey());
        int vallen = strlen(curr->getValue());
        SAPDB_strcpy(line, curr->getKey() );
        line[keylen] = '=';
        SAPDB_strcpy(line+keylen+1, curr->getValue() );
        // write the line        
        sqlfwritec( fhandle, line, keylen+vallen+1, &ferror);
        curr = curr->getNext();
    }
    
    sqlfclosec( fhandle, sp5vf_close_normal, &ferror);    
    if( ferror.sp5fe_result != vf_ok ) {
        storeReason(ferror.sp5fe_text);
        return false;
    }
    else
        return true;
}

SAPDB_Bool Tools_Properties::store( const Tools_DynamicUTF8String& rawfilename ) {
    Tools_DynamicUTF8String temp(filename);
    setFileName( rawfilename );
    if( !store() ) {
        // set old file name again
        setFileName( temp );
        return false;
    }
    return true;
}

void Tools_Properties::setProperty( const Tools_DynamicUTF8String& key, const Tools_DynamicUTF8String& value ) {
    char* pkey = getCharPointer(key);
    char* pvalue =  getCharPointer(value);

    Tools_Property* old = new Tools_Property( pkey, pvalue );
    first = insertProperty(first, new Tools_Property( pkey, pvalue ));

    delete[] pkey;
    delete[] pvalue;
}

Tools_DynamicUTF8String Tools_Properties::getProperty( const Tools_DynamicUTF8String& key ) const {
    Tools_DynamicUTF8String tds;
    char* pkey = getCharPointer(key);
    Tools_Property* p = findProperty(pkey);
    if( p != NULL )
        tds = _Tools_UTF8Ptr(p->getValue());

    delete[] pkey;
    return tds;
}

Tools_DynamicUTF8String Tools_Properties::getProperty( const Tools_DynamicUTF8String& key, const Tools_DynamicUTF8String& defval ) const {
    Tools_DynamicUTF8String realval(getProperty(key));
    if( realval.Empty() )
        return defval;
    else
        return realval;
}

Tools_Property* Tools_Properties::findProperty( const char* key ) const {
    for( Tools_Property* curr = first; curr != NULL; curr = curr->getNext() ) {
        if( 0 == strcmp(curr->getKey(), key) )
            return curr;
    }
    return NULL;
}

Tools_Property* Tools_Properties::insertProperty(Tools_Property* start,
                                                 Tools_Property* newp) const {
    if( start == NULL )
        return newp;
    if( newp == NULL )
        return start;  

    const char* newkey = newp->getKey();
    Tools_Property* curr(NULL);

    curr = findProperty(newkey);
    if (curr != NULL ) {
        // key already exists
        curr->setValue(newp->getValue());
        delete newp;
        return start;
    }

    // key of new property is smaller than start
    if( strcmp(newkey, start->getKey()) < 0 ) {
        // newp will be predecessor of start
        newp->setNext(start);
        start = newp;
        return start;
    }

    curr = start;
    while( curr != NULL ) {
        Tools_Property* currnext = curr->getNext();
        // check if newp is between curr and currnext
        if( strcmp(newkey,curr->getKey() ) > 0 ) {
            if( currnext == NULL ) {
                // newp will be the last one
                curr->setNext(newp);
                newp->setNext(NULL);
                return start;
            }
            else if( strcmp(newkey, currnext->getKey()) < 0 ) {
                newp->setNext(currnext);
                curr->setNext(newp);
                return start;
            }
        }
        curr = currnext;
    }
    // we should never get here...
    return NULL;
}

void Tools_Properties::setFileName( const Tools_DynamicUTF8String& s ) {
    if( filename != NULL )
        delete[] filename;
    filename = getCharPointer(s);
}

void Tools_Properties::dump(FILE* const f) const {
    char line[8192];
    Tools_Property* curr(first);
    
    // dump file name if there is one
    if( filename != NULL )
        fprintf(f, "properties (%s):\n", filename);
    else
        fprintf(f, "properties (%s):\n", "undefined location");

    // dump properties
    while( curr != NULL ) {
        int keylen = strlen(curr->getKey());
        int vallen = strlen(curr->getValue());
        SAPDB_strcpy(line, curr->getKey() );
        line[keylen] = '=';
        SAPDB_strcpy(line+keylen+1, curr->getValue() );
        fprintf(f, "%s\n", line);
        curr = curr->getNext();
    }
    fprintf(f, "\n");
}

void Tools_Properties::storeReason( tsp00_C40 text ) {
    strncpy(errReason, text, 39);
}

char* Tools_Properties::getCharPointer( const Tools_DynamicUTF8String& s ) const {
    // there seems to be no const method in Tools_DynamicUTF8String that returns the
    // string as a char*...
    char* p = NULL;
    int size = s.Size();
    p = new char[size+1];
    *(p+size) = 0;
    SAPDB_memcpy(p, s.Data(), size);
    return p;
}
