/**********
 * Copyright (c) 2004 Greg Parker.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **********/

#include "includes.h"
#include "prefs.h"


// There is one global default prefs list. 
// There is also a set of override values for each connection, mostly 
// for terminal settings.

// Each preference is a RESOURCE
// res type: 'dflt' or some ID for a connection override
// res id: which pref

/* 
   interface: 
   echo password (NO)
   keyboard pane(s)
   
   terminal:
   TERM type ("xterm")
   scrollback line count (500?)
   scroll down on input (NO)
   scroll down on keypress (YES)
   
   ssh:
   compression (NO) (fixme implement)
   ciphers ("aes128-cbc,3des-cbc")
   authmethod (fixme implement)
   
   putty terminal:
   wrap_mode (YES==autowrap)
   dec_om "DEC Origin Mode" (YES)
   lfhascr "Implicit CR in every LF" (NO)
   bce "Background Color Erase" (?)
   blink_text "Blink" (NO)
   answerback "Answerback to ^E" ("pssh")
   localecho (NO)
   localedit (NO)
   printing (NO)

   putty keyboard:
   bksp_is_delete "Backspace is DEL" (YES==^? rather than ^H)
   rxvt_homeend "rxvt Home and End" (NO)
   funky_type "Function Keys" (DIGITAL, Linux, xterm, VT400, VT100+, SCO)
   app_cursor "Application Cursor Keys" (NO?)
   app_keypad "Application Keypad" (NO?)
   nethack "NetHack Keypad" (NO)
   compose (NO) (use real compose key on keyboard?)
   ctrlalt (NO)
   
   lots more putty...
   
*/


#define noRecord ((UInt16)0xffff)


static DmOpenRef PrefsDB = 0;
// fixme implement overrides

extern DmOpenRef OpenDB(UInt32 type, char *name, Boolean resDB, Boolean create);

int PrefsInit(void)
{
    PrefsDB = OpenDB('Pref', "pssh Preferences", true, true);
    return (PrefsDB != 0);
}


static MemHandle PrefsGetHandle(uint32_t which, Boolean fallback)
{
    UInt16 index;

    // fixme search override here unless fallback==false

    // search default prefs
    index = DmFindResource(PrefsDB, 'dflt', which, NULL);
    if (index == noRecord) return NULL;

    return DmGetResourceIndex(PrefsDB, index);
}


uint32_t PrefsGetInt(uint32_t which, uint32_t defaultValue)
{
    uint32_t result;
    uint32_t *p;
    MemHandle h = PrefsGetHandle(which, true);
    if (!h) return defaultValue;
    if (MemHandleSize(h) != 4) return defaultValue;
    
    p = MemHandleLock(h);
    result = *p;
    MemHandleUnlock(h);
    DmReleaseResource(h);

    return result;
}


void PrefsPutInt(uint32_t which, uint32_t value)
{
    uint32_t *p;
    MemHandle h = PrefsGetHandle(which, false);
    if (h  &&  MemHandleSize(h) == sizeof(value)) {
        // resource exists, and it's the right size
        p = MemHandleLock(h);
        DmWrite(p, 0, &value, sizeof(value));
        MemHandleUnlock(h);
        DmReleaseResource(h);
        return;
    }
    else if (h) {
        // resource exists, but its the wrong size
        h = DmResizeResource(h, sizeof(value));
        p = MemHandleLock(h);
        DmWrite(p, 0, &value, sizeof(value));
        MemHandleUnlock(h);
        DmReleaseResource(h);
    }
    else {
        // resource does not exist
        h = DmNewResource(PrefsDB, 'dflt', which, sizeof(value));
        p = MemHandleLock(h);
        DmWrite(p, 0, &value, sizeof(value));
        MemHandleUnlock(h);
        DmReleaseResource(h);
    }
}


char *PrefsGetString(uint32_t which, char *defaultValue)
{
    char *result;
    char *p;
    size_t size;
    MemHandle h = PrefsGetHandle(which, true);
    if (!h) return arena_strdup(defaultValue);
    size = MemHandleSize(h);
    
    p = MemHandleLock(h);
    result = arena_malloc(size + 1); // be paranoid about nul terminator
    memcpy(result, p, size);
    result[size] = '\0';
    MemHandleUnlock(h);
    DmReleaseResource(h);

    return result;
}


void PrefsPutString(uint32_t which, char *value)
{
    char *p;
    size_t sizeNeeded = strlen(value)+1;
    MemHandle h = PrefsGetHandle(which, false);
    if (h  &&  MemHandleSize(h) >= sizeNeeded) {
        // resource exists, and it's big enough
        p = MemHandleLock(h);
        DmWrite(p, 0, value, sizeNeeded);
        MemHandleUnlock(h);
        DmReleaseResource(h);
        return;
    }
    else if (h) {
        // resource exists, but it's the wrong size
        h = DmResizeResource(h, sizeNeeded);
        p = MemHandleLock(h);
        DmWrite(p, 0, value, sizeNeeded);
        MemHandleUnlock(h);
        DmReleaseResource(h);        
    }
    else {
        // resource does not exist
        h = DmNewResource(PrefsDB, 'dflt', which, sizeNeeded);
        p = MemHandleLock(h);
        DmWrite(p, 0, value, sizeNeeded);
        MemHandleUnlock(h);
        DmReleaseResource(h);
    }
}

