//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// 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 3 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "parse.h"
#include "mode.h"
#include "utils.h"
#include "conf.h"

extern char tmp[MAXMAXLEN];

static mode *modes	  = NULL;
static mode *currentMode  = NULL;
static mode *internalMode = NULL;
static mode *defMode	  = NULL;

mode* getInternalMode()
{
    if (internalMode == NULL) {
    
        internalMode = (mode*) calloc(1, sizeof(mode));
	
        internalMode->name   = stringNew("_INTERNAL_");
        internalMode->parent = NULL; // no parent mode by default
        internalMode->next   = NULL;
        internalMode->keys   = NULL;
    }
    
    return internalMode;
}

mode* getDefaultMode()
{
    if (!defMode) {	// We should have at least default mode
        
        defMode = (mode*) calloc(1, sizeof(mode));
	
        defMode->name   = stringNew("default");
        defMode->parent = NULL; // no parent mode by default
        defMode->next   = NULL;
        defMode->keys   = NULL;
 
        modes	      = defMode;
        currentMode   = defMode;
    }
    
    return defMode;
}

mode* getCurrentMode()
{
    return currentMode;
}

void setCurrentMode(mode* m)
{
    //DEBUG2("[EX]: setCurrentMode() %s",(m ? m->name : "NULL"));
    currentMode = m;
}

char *getModeName (void)
{
    return (currentMode == NULL ? NULL : currentMode->name->str);
}

mode* findMode(const char *name)
{
    mode *mp = modes;
    while (mp && strcmp(name, mp->name->str) != 0) {
        mp = (mode*) mp->next;
    }
    //if (mp) {
    //	DEBUG2("[EX]: findMode() %s = %s",name,mp->name->str);
    //}
    return mp;
}

mode* addMode(const char *name, const char *parent)
{
    mode* nm = (mode*) calloc(1, sizeof(mode));
    nm->name = stringNew(name);
    nm->keys = NULL;

    if (parent) {
    	nm->parent = stringNew(parent);
    }

    // Insert in head
    if (modes == NULL) {   // first elem
    	nm->next = NULL;
    } else {
    	nm->next = (mode*) modes;
    }
    modes = nm;
    
    return nm;
}


void switchMode(const char *modeName)
{
    if (modeName ==  NULL) {
        logger(L_DBG, "[EX]: setMode() NULL input");
        return;
    }
    DEBUG2("[EX]: setMode() to %s", modeName);

    mode *mp = findMode(modeName);

    if (mp) {
        currentMode = mp;
        DEBUG2("[EX]: setMode() new mode was set to %s", mp->name->str);
    } else {
        logger(L_DBG, "[EX]: setMode() new mode did not found");
    }

    return;
}

mode* getModes()
{
    return modes;
}

void forgetModes()
{
    defMode = currentMode = modes = NULL;
}


void freeInternalMode()
{
    if (internalMode) {
        type_key *mki = internalMode->keys;
        while (mki) {
            type_key* It = mki;
            mki = (type_key*) mki->next;

            freeCmds(getCommand(It));
            It->commands = NULL;
	    
            free(It->key);
            free(It);
        }
	
        stringFree(internalMode->name,  BOOL_YES);
        internalMode->name = NULL;
	
        if (internalMode->parent) {
	    stringFree(internalMode->parent,BOOL_YES);
	    internalMode->parent = NULL;
	}
	
        free(internalMode);
        internalMode = NULL;
    }
}

//
// if parameter is NULL use modes static var
//
void freeModes(mode* fmodes)
{
    //logger(L_DBG, "[EX]: freeModes()");
    mode * pmodes = (fmodes ? fmodes : modes);

    // Keys
    while (pmodes) {
        mode	 *md = pmodes;
        //printf("mode -> %s\n",md->name);
        type_key *mk = md->keys;

        while (mk) {
            type_key* It = mk;
            mk = (type_key*) mk->next;

            freeCmds(getCommand(It));
            It->commands = NULL;
	    
            free(It->key);
            free(It);
        }
        pmodes = (mode*) pmodes->next;
	
        stringFree(md->name,  BOOL_YES);
        md->name = NULL;
        if (md->parent) {
	        stringFree(md->parent,BOOL_YES);
	        md->parent = NULL;
        }
	
        free(md);
	    md = NULL;
    }
    
    if (!fmodes) {  // all modes were deleted
        forgetModes();

        // special handling for "internal" mode
        freeInternalMode();
    }
}

void printModes()
{
    mode *mp = modes;
    while (mp) {

        if (!mp->parent) {
            sprintf(tmp, "%s %s", MODE_STR, mp->name->str);
        } else {
            sprintf(tmp, "%s %s : %s", MODE_STR, mp->name->str, mp->parent->str);
        }
        logger(L_CFG, tmp);

        printKeys(mp->keys);

        sprintf(tmp, "%s %s\n", MODE_END_STR, mp->name->str);
        logger(L_CFG, tmp);

        mp = (mode*)mp->next;
    }

    return;
}
