/* treewm - an X11 window manager.
 * Copyright (c) 2001-2002 Thomas Jäger <TheHunter2000 at web dot de>
 * This code is released under the terms of the GNU GPL.
 * See the included file LICENSE for details.
 */

#include "resmanager.h"
#include "sceme.h"
#include "clientinfo.h"
#include "menuinfo.h"
#include "action.h"
#include "desktop.h"
#include <X11/xpm.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h> // for isspace


ResManager *rman;


ResManager::ResManager(char *Config) : ClientFlags() {
  for (int i=0;i!=SE_NUM;++i)
    infos[i] = new InfoList;
  rman = this; // shit
  config = Config;
  pixmaps = 0;

  sections = new Section;
  sections->type = SE_SCEME; // We definitely need a default sceme
  sections->name = 0;
  sections->fe = 0;
  sections->next = 0;

  LoadCFGFile();
  CreateScemes();
  DeleteSections();
  for (int i = 0; i!=CLIENTFLAGNUM; ++i)
    ClientFlags[ClientFlagArray[i].str] = ClientFlagArray[i].flag;
  for (int i = 0; i!=FLAGACTNUM; ++i)
    FlagActFlags[FlagActArray[i].str] = FlagActArray[i].flag;

}

int ResManager::GetFlagAct(char *str) {
  FlagMapIter i = FlagActFlags.find(str);
  if (i == FlagActFlags.end())
    return 0;
  return i->second;
}

unsigned long ResManager::GetFlag(char *str) {
  FlagMapIter i = ClientFlags.find(str);
  if (i == ClientFlags.end())
    return 0;
  return i->second;

}


ResManager::~ResManager() {
  for (int t=0;t!=SE_NUM;++t) {
    int n = infos[t]->size();
    char *a[n];
    int k = 0;
    for (InfoListIter i = infos[t]->begin();i != infos[t]->end();++i) {
      a[k] = (char *)i->first;
      delete i->second;
      ++k;
    }
    delete infos[t];
    for (k=0;k!=n;++k)
      free(a[k]);
  }

  RPixmap *ps;
  for (;pixmaps;pixmaps=ps) {
    ps = pixmaps->next;
    delete pixmaps;
  }

}



Info *ResManager::GetInfo(int type, char *name) {
  InfoListIter i = infos[type]->find(name);
  if (i == infos[type]->end())
    return 0;
  return i->second;
}

void ResManager::CreateScemes() {
  int t;
  Info* info = 0;
  for (Section *s=sections;s;s=s->next) {
    t = s->type;
    switch (t) {
      case SE_CLIENTINFO:
        info = new ClientInfo(s);
        break;
      case SE_SCEME:
        info = new Sceme(s);
        break;
      case SE_ACTION:
        info = new Action(s);
        break;
      case SE_MENUINFO:
        info = new MenuInfo(s);
        break;
      default:
        exit(1);
    }
    char *c =strdup(s->name ? s->name : "");
    (*infos[t])[c] = info;
  }

  for (int t=0;t!=SE_NUM;++t) {
    for (InfoListIter i = infos[t]->begin();i != infos[t]->end();++i) {
      i->second->Init();
    }
  }

  // Dirty hack
  for (InfoListIter i = infos[SE_MENUINFO]->begin();i != infos[SE_MENUINFO]->end();++i) {
    MenuInfo *mi = (MenuInfo *)i->second;
    for (int j=0; j!=mi->n; ++j) {
      MenuItem &mitem = mi->menu[j];
      if (mitem.key[0] == '$') {
        lower(mitem.key);
        MenuInfo *mi2 = (MenuInfo *)rman->GetInfo(SE_MENUINFO,mitem.key+1);
        if (mi2) {
          mitem.submenu = new SubMenuInfo;
          mitem.submenu->menu = mi2->menu;
          mitem.submenu->num  = mi2->n;
          free(mitem.key);
          mitem.key = strdup("");
        }
      }
    }
  }



}


RPixmap *ResManager::GetPixmap(const char *name) {
  RPixmap *last=0;
  for (RPixmap *pm = pixmaps;pm;pm=pm->next) {
    if (!strcmp(pm->path,name))
      return pm;
    last = pm;
  }
  if (last) {
    last->next = new RPixmap();
    last = last->next;
  } else {
    pixmaps = new RPixmap();
    last = pixmaps;
  }
  last->n = 0;
  last->path = new char[strlen(name)+1];
  strcpy(last->path,name);
  last->pm = 0;
  last->next = 0;

  return last;
}

Pixmap RPixmap::GetPixmap() {
  if (!n++) {
    Pixmap dummy;
    char buf[256];
    if (path[0] == '/') {
      buf[0] = '\0';
    } else {
      strcpy(buf,PIXMAPS);
      strncat(buf,path,255);
    }
    int r;
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    XGetWindowAttributes(dpy,root,&root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 40000; /* Allow for "similar" colors */
    xpm_attributes.valuemask = XpmSize | /*XpmReturnPixels |*/ // bullshit
                               XpmColormap | XpmCloseness;
    r = XpmReadFileToPixmap(dpy, root, buf[0] ? buf : path, &pm, &dummy,&xpm_attributes);
    if (r!=XpmSuccess) {
      n = 0;
      return 0;
    }
    if (dummy)
      XFreePixmap(dpy,dummy);
    w = xpm_attributes.width;
    h = xpm_attributes.height;
  }
  return pm;
}

void RPixmap::FreePixmap() {
  if (--n > 0)
    return;
  n = 0;
  if (pm)
    XFreePixmap(dpy,pm);
}

RPixmap::~RPixmap() {
  if (pm && n)
    XFreePixmap(dpy,pm);
  if (path)
    delete [] path;
}


Section *ResManager::GotoSection(char *type,char *name) {
  int newtype = -1;
  if (!strcasecmp(type, "client"))
    newtype = SE_CLIENTINFO;
  if (!strcasecmp(type, "sceme"))
    newtype = SE_SCEME;
  if (!strcasecmp(type, "action"))
    newtype = SE_ACTION;
  if (!strcasecmp(type, "menu"))
    newtype = SE_MENUINFO;
  if (newtype == -1)
    return 0;
  Section *s,*last=0;
  for (s=sections;s;s=s->next) {
    if (newtype == s->type &&
        ((!name && !s->name) ||
         (name && s->name && !strcasecmp(name,s->name)) )) {
      return s;
    }
    last = s;
  }
  s = new Section;
  s->type = newtype;
  s->fe = 0;
  s->next = 0;
  if (name) {
    s->name = new char[strlen(name)+1];
    strcpy(s->name,name);
    lower(s->name);
  } else {
    s->name = 0;
  }
  if (last)
    last->next=s; else
    sections = s;
  return s;

}


void ResManager::LoadCFGFile() {
// This is mainly stolen from xawtv

  Section *s=0;
  char filename[256];
  if (config)
    strncpy(filename,config,255); else
    snprintf(filename,255,"%s/%s",getenv("HOME"),".treewm");
  filename[255] = '\0';

  char line[256],tag[64],value[192];
  FILE *fp;
  int nr,k;

  if (NULL == (fp = fopen(filename,"r"))) {
    fprintf(stderr,"Warning: no config file (~/.treewm) found\n");
    return;
  }

  nr = 0;
  while (NULL != fgets(line,255,fp)) {
    ++nr;
    if (line[0] == '\n' || line[0] == '#' || line[0] == '%')
      continue;
    for (k=1;k!=255;++k)
      if (line [k] == '\0' || line[k] == '\n' || line[k] == '#' || line[k] == '%') {
        line[k] = '\0';
        break;
      }
    for (--k;k;--k)
      if (!isspace(line[k])) {
        line[k+1] = '\0';
        break;
      }
    if (!k)
      continue;

    k = sscanf(line,"[%63[^] ] %191[^]]]",tag,value);

    if (k) {
      /* [type name] */
      s = GotoSection(tag,(k == 2) ? value : 0);
      if (!s) {
        fprintf(stderr,"%s,%d: not a valid section specifier\n",filename,nr);
        continue;
      }
    } else {
      k = sscanf(line," %63[^= ] = %191[^\n]",tag,value);
      if (k) {
      /* tag = value */
        if (s) {
          if (strcasecmp(tag,"include")) {
            if (k==2)
              s->SetEntry(tag,value);
          } else {
            s->IncludeSection((k == 2) ? value : 0);
          }
        } else {
          fprintf(stderr,"%s:%d: error: no section\n",filename,nr);
        }
      } else {
        /* Huh ? */
        fprintf(stderr,"%s:%d: syntax error\n",filename,nr);
      }
    }
  }
  fclose(fp);
  return;
}

void ResManager::DeleteSections() {
  Section *saves;
  for (Section *s=sections;s;s=saves) {
    saves = s->next;
    delete s;
  }
}


void Section::SetEntry(char *tag,char *value) {
  Entry *e,*last=0;
  for (e=fe;e;e=e->next) {
    if (!strcasecmp(tag,e->tag)) {
      if (e->value)
        delete [] e->value;
      e->value = new char[strlen(value)+1];
      strcpy(e->value,value);
      return;
    }
    last = e;
  }
  e = new Entry;
  e->tag = new char[strlen(tag)+1];
  strcpy(e->tag,tag);
  e->value = new char[strlen(value)+1];
  e->next = 0;
  strcpy(e->value,value);
  if (last)
    last->next=e; else
    fe = e;
  return;
}

char *Section::GetEntry(char *tag) {
  for (Entry *e=fe;e;e=e->next)
    if (!strcasecmp(tag,e->tag))
      return e->value;
  return 0;
}

bool Section::GetIntEntry(char *tag,int &ret) {
  char *str = GetEntry(tag);
  if (!str)
    return false;
  ret = atoi(str);
  if (!ret) {
    if (!strcasecmp(str,"on") || !strcasecmp(str,"true") || !strcasecmp(str,"yes"))
      ret = 1;
  }
  return true;
}

void Section::IncludeSection(char *name) {
  Section *s;
  for (s = rman->sections;;s=s->next) {
    if (type == s->type &&
        ((!name && !s->name) ||
         (name && s->name && !strcasecmp(name,s->name)) ))
      break;
    if (!s)
      return;
  }

  for (Entry *e=s->fe;e;e=e->next)
    SetEntry(e->tag,e->value);

}


Section::~Section() {
  Entry *savee;
  if (name)
    delete [] name;
  for (Entry *e=fe;e;e=savee) {
    savee = e->next;
    if (e->tag)
      delete [] e->tag;
    if (e->value)
      delete [] e->value;
    delete e;
  }
}


syntax highlighted by Code2HTML, v. 0.9.1