/**********
 * Copyright (c) 2003-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 "recordlist.h"
#include "rsrc/rsrc.h"
#include "formutils.h"

#include "connectionlist.h"

/* connection record format:
   1 byte length
   n bytes hostname\0
   1 byte length
   n bytes portname\0 (empty defaults to "22")
   1 byte length
   n bytes username\0
*/

#define ConnectionDBName "pssh Connection List"
#define ConnectionDBType 'Conn'
static DmOpenRef ConnectionDB = 0;
static RecordList *ConnectionList = NULL;


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

static Boolean ReadConnectionRecord(char *recordP, char **hostname, char **portname, char **username) CONNECTIONLIST_SEGMENT;
static void DrawConnectionRecord(MemPtr recordP, UInt16 index, RectanglePtr bounds)  CONNECTIONLIST_SEGMENT;


Boolean ConnectionListInit(void)
{
    ConnectionDB = OpenDB(ConnectionDBType, ConnectionDBName, false, true);
    if (!ConnectionDB) return false;

    ConnectionList = 
        RecordListNew(ConnectionDB, MainFormID, MainConnectionTableID, 
                      MainConnectionScrollbarID, DrawConnectionRecord);
    if (!ConnectionList) return false;

    return true;
}


void ConnectionListFree(void)
{
    if (ConnectionList) RecordListFree(ConnectionList);
    if (ConnectionDB)   DmCloseDatabase(ConnectionDB);
}

void ConnectionListUpdate(void) {
    RecordListUpdate(ConnectionList);
}

UInt16 ConnectionListSelectedIndex(void) {
    return RecordListSelectedIndex(ConnectionList);
}

void ConnectionListSetSelectedIndex(UInt16 index) {
    RecordListSetSelectedIndex(ConnectionList, index);
}

void ConnectionListClearSelection(void) {
    RecordListClearSelection(ConnectionList);
}

void ConnectionListDeleteSelectedRecord(void) {
    RecordListDeleteSelectedRecord(ConnectionList);
}

Boolean ConnectionListHandleEvent(EventPtr event) {
    return RecordListHandleEvent(ConnectionList, event);
}



// returned values are only usable until recordP is unlocked
static Boolean ReadConnectionRecord(char *recordP, char **hostname, 
                                    char **portname, char **username)
{
#define CHECK_SPACE(n) do { if (p+(n)>end) goto bad; } while (0)

    Boolean ok = true;
    uint8_t *p = recordP;
    uint8_t *end = p + MemPtrSize(recordP);
    uint8_t len;

    // hostname field
    CHECK_SPACE(1);
    len = *p++;
    CHECK_SPACE(len);
    if (p[len-1] != '\0') goto bad;
    if (hostname) *hostname = p;
    p += len;

    // port number field
    CHECK_SPACE(1);
    len = *p++;
    CHECK_SPACE(len);
    if (p[len-1] != '\0') goto bad;
    if (portname) {
        if (len > 1) {
            *portname = p;
        } else {
            // no port specified - use default port
            *portname = "22";
        }
    }
    p += len;

    // username field
    CHECK_SPACE(1);
    len = *p++;
    CHECK_SPACE(len);
    if (p[len-1] != '\0') goto bad;
    if (username) *username = p;
    p += len;

    return true;

 bad: 
    return false;

#undef CHECK_SPACE
}


MemHandle ConnectionListReadSelectedRecord(char **hostname, char **portname, char **username)
{
    MemHandle recordH;
    MemPtr recordP;
    Boolean ok;

    recordH = RecordListQuerySelectedRecord(ConnectionList);
    if (!recordH) return false;

    recordP = MemHandleLock(recordH);
    ok = ReadConnectionRecord(recordP, hostname, portname, username);
    
    if (!ok) {
        MemHandleUnlock(recordH);
        return NULL;
    } else {
        return recordH;
    }
}


Boolean ConnectionListWriteSelectedRecord(FieldPtr hostFld, FieldPtr portFld, FieldPtr userFld)
{
    Boolean ok = true;
    MemHandle recordH;
    UInt32 size;

    // resize record and write data to it
    
    size = (1+FldGetTextLength(hostFld)+1 + 
            1+FldGetTextLength(portFld)+1 + 
            1+FldGetTextLength(userFld)+1);
    
    recordH = RecordListGetSelectedRecord(ConnectionList, size);
    if (!recordH) {
        // error resizing or opening record
        // complain, leave existing record intact, don't connect
        // fixme
        ok = false;
    } else {
        Err err = 0;
        UInt32 written = 0;
        MemPtr recordP = MemHandleLock(recordH);
        if (!err) err = PrvStoreFieldToRecord(hostFld, recordP, &written);
        if (!err) err = PrvStoreFieldToRecord(portFld, recordP, &written);
        if (!err) err = PrvStoreFieldToRecord(userFld, recordP, &written);
        MemHandleUnlock(recordH);
        RecordListReleaseRecord(ConnectionList, recordH, true);
        if (err) {
            // error writing record
            // complain, delete record, don't connect
            // fixme
            ok = false;
        }
    }

    return ok;
}



static void DrawConnectionRecord(MemPtr recordP, UInt16 index, 
                                 RectanglePtr bounds)
{
    char *hostname;
    char *portname;
    char *username;

    if (ReadConnectionRecord(recordP, &hostname, &portname, &username)) 
    {
        // "username@hostname"
        // "username@hostname:portname"  (non-default port only)
        int x = bounds->topLeft.x + 1;
        int y = bounds->topLeft.y;
        int len;
        char *buf;
        int showPort;

        showPort = (0 != strcmp(portname, "") && 0 != strcmp(portname, "22"));

        len = strlen(username) + strlen("@") + strlen(hostname);
        if (showPort) {
            len += strlen(":") + strlen(portname);
        }

        buf = arena_malloc(len + 1);
        strcpy(buf, username);
        strcat(buf, "@");
        strcat(buf, hostname);
        if (showPort) {
            strcat(buf, ":");
            strcat(buf, portname);
        }
        
        WinDrawTruncChars(buf, len, x, y, 
                          bounds->topLeft.x + bounds->extent.x - x - 1);
    }
}

