/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/


#include "window/window.hh"
#include "app/app.hh"
#include "main/main.hh"
#include "window/style.hh"
#include "file/file.hh"
#include "loaders/load.hh"
#include "window/wmanager.hh"
#include "gui/text_input.hh"
#include "gui/list_box.cc"
#include "app/app.hh"
#include "menu/textitem.hh"
#include "video/display.hh"
#include "loaders/load.hh"
#include "gui/text_input.hh"
#include "gui/text.hh"
#include "font/anti_prop.hh"
#include "window/colorwin.hh"
#include "gui/button.hh"
#include "install.hh"
#include "app/registry.hh"
#include "file/get_filename.hh"
#include "gui/text_scroll.hh"
#include "loaders/wav_load.hh"
#include "loaders/mp3_load.hh"
#include "lisp/lisp.hh"
#include "status/status.hh"
#include "../../golg/g1_file.hh"
#include "status/gui_stat.hh"

static i4_window_manager_class *wm;
static i4_graphical_style_class *style;
static i4_list_box_class *display_picker, *size_picker;
static i4_text_scroll_window_class *error_win;
static char inst_path[200];
int inst_sizes[3]={20,40,60};

char *reg_key="SOFTWARE\\Crack dot Com\\Golgotha\\1.0";
char *i4_display_key="SOFTWARE\\Crack dot Com\\I4\\1.0";
char *sfx_key="SOFTWARE\\Crack dot Com\\i4\\1.0";

i4_file_class *uninstall_script=0;

int installed=0;
int col2=120;
int size_needed_to_install=0;

i4_text_input_class *path=0;


enum { 
  BROWSE_OK,
  BROWSE_CANCEL,
  BROWSE,
  SAVE_AND_QUIT,
  SAVE_AND_RUN,
  INSTALL,
  QUIT,
  COMPACT,
  TYPICAL,
  UNINSTALL,
  DISPLAY_SELECT
};



void fix_path(char *f)
{
  for (;*f; f++)
#ifdef __linux
    if (*f=='\\')
      *f='/';
#else
    if (*f=='/')
      *f='\\';
#endif

}


#ifdef __linux
int free_space(char *path)
{
  reutn 100*1024*1024;
}
#else

#include 
// int free_space(char *path)
// {
//   DWORD spc, bpc, bps, nfc, tnc;
//   GetDiskFreeSpace(path, &spc, &bpc, &bps, &nfc, &tnc);

//   return  nfc * tnc * bpc;
// }
#endif


class setup_options : public i4_color_window_class
{
public:
  setup_options(w16 w, w16 h, i4_graphical_style_class *style)
    : i4_color_window_class(w,h, style->color_hint->neutral(),style) {}

  void parent_draw(i4_draw_context_class &context)
  {
    undrawn_area.add_area(0,0, width()-1, height()-1);

    i4_color_window_class::parent_draw(context);

    i4_font_class *f=style->font_hint->normal_font;

    char p[200];
    i4_os_string(*(path->get_edit_string()), p, 200);

    char display[200];
    i4_text_item_class *ti=(i4_text_item_class *)display_picker->get_current_item();
    i4_os_string(ti->get_text(), display, 200);

    char size[200];
    ti=(i4_text_item_class *)size_picker->get_current_item();
    i4_os_string(ti->get_text(), size, 200);

    char str[500];
    sprintf(str,
            "Installation Options:\n"
            "  Display           : %s\n"
            "  Installation Path : %s\n"
            "  Install Size      : %s\n\n", display, p, size);

    switch (size_picker->get_current())
    {
      case 0 :
        size_needed_to_install=40*1024*1024;
        li_set_value("install_size", li_get_symbol("compact"));
        sprintf(str+strlen(str), 
                "Small Installation does not have music"); break;
        break;

      case 1 :
        size_needed_to_install=60*1024*1024;
        li_set_value("install_size", li_get_symbol("typical"));
        sprintf(str+strlen(str), 
                "Large Installation has the best of everything"); break;
        break;
    }


  
    f->set_color(0);
    f->put_paragraph(local_image, 6,6, str,
                     context, 1, i4_font_class::LEFT,  width());

    f->set_color(0xffffff);
    f->put_paragraph(local_image, 5,5, str,
                     context, 1, i4_font_class::LEFT,  width());
  }
  char *name() { return "options"; }
};

setup_options *setup_window=0;


#ifdef _WINDOWS
/*
 * build a shortcut in the start menu
 */
void MakeShortcut()
{
  char buf[512];
  char szSetupIni[MAX_PATH];
  int len;
  int fh;

  static char setup_ini[] =
    "[progman.groups]\r\n"
    "groupX=Programs\r\n"
    "[groupX]\r\n"
    "\"Golgotha\",\"golgotha.exe\",,,,\"%s\"\r\n";

  GetWindowsDirectory(szSetupIni, sizeof(szSetupIni));
  sprintf(szSetupIni+strlen(szSetupIni), "\\SETUP.INI");

 
  fix_path(buf);

 
  len = wsprintf(buf, setup_ini, inst_path);
  fh = _lcreat(szSetupIni, 0);

  if (fh != -1)
  {
    _lwrite(fh, buf, len);
    _lclose(fh);
    WinExec("grpconv -o", SW_HIDE);
  }
}
#endif
 


void setup_changed()
{
  if (setup_window)
    setup_window->request_redraw(i4_F);
  error_win->clear();
}

static i4_bool no_space=i4_F;


void write_header(i4_file_class *fp, i4_sound_info &fmt)
{
  fp->write("RIFF",4);
  fp->write_32(36+fmt.size);       // 36 + snd->size
  fp->write("WAVE", 4);
  fp->write("fmt ", 4);

  fp->write_32(16);        // size of chunk

  fp->write_16(1);         // data type

  fp->write_16(fmt.channels);           // num channels
  fp->write_32(fmt.sample_rate);   // sample rate
  fp->write_32(fmt.sample_rate*
               fmt.sample_size*fmt.channels);       // snd->sample_rate * snd->sample_size
  fp->write_16(fmt.sample_size * fmt.channels);
  fp->write_16(fmt.sample_size*8);


  fp->write("data",4);
  fp->write_32(fmt.size);       // snd->size
}



char *forward_slash(char *f)
{
  char *r=f;
  for (;*f; f++)
    if (*f=='\\')
      *f='/';
  return r;
}




li_object *li_uninstall_cmd(li_object *o, li_environment *env)
{
  if (uninstall_script)
  {
    li_object *c=li_eval(li_car(o,env),env);
    li_get_type(c->type())->print(c, uninstall_script);
    return li_true_sym;
  }
  return li_nil;
}


li_object *li_delete(li_object *o, li_environment *env)
{
  char p[200];
  strcpy(p,li_get_string(li_eval(li_car(o,env), env),env));
  fix_path(p);


  if (i4_unlink(p))
    return li_true_sym;
  else return li_nil;
}

li_object *li_mkdir(li_object *o, li_environment *env)
{
  char *fn=li_get_string(li_eval(li_car(o,env),env),env);
  char p[200];
  sprintf(p, "%s/%s", inst_path, fn);
  fix_path(p);

  if (i4_mkdir(p))
  {
    if (uninstall_script)
      uninstall_script->printf("(rmdir \"%s\")\n", forward_slash(p));
    
    return li_true_sym;
  }
  else
    return 0;
}


li_object *li_rmdir(li_object *o, li_environment *env)
{
  char *fn=li_get_string(li_eval(li_car(o,env),env),env);
  fix_path(fn);

  i4_directory_struct ds;

  if (i4_get_directory(fn, ds))
  {
    int i;
    char f2[256], f[256];
    for (i=0; iread(inm, s.size);

    float x_end=s.size/2;
    float x=0, x_step=s.sample_rate/(float)rate;
    int out_size=(int)((double)s.size * (double)rate / (double)s.sample_rate), outc=0;
    int out_samples=out_size/2;


    w16 *outm=(w16 *)i4_malloc(out_size,"");

    
    while (outcwrite(outm, out_size);

    i4_free(outm);
    i4_free(inm);
  }

  delete in;
  delete out;
}

li_object *mp3_2_wav(li_object *o, li_environment *env)
{
  char *fn=li_get_string(li_eval(li_car(o,env),env),env);
  i4_filename_struct split;

  int resample_rate=-1;
  if (li_cdr(o,env))
    resample_rate=li_get_int(li_eval(li_second(o,env),env),env);


  i4_split_path(fn, split);
  char out_name[256], tmp_name[256];

  sprintf(out_name, "%s/%s/%s.wav", inst_path, split.path, split.filename);
  sprintf(tmp_name, "%s/%s/%s_tmp.wav", inst_path, split.path, split.filename);
  

  fix_path(out_name);
  fix_path(tmp_name);

  
  i4_file_class *out=i4_open(resample_rate==-1 ? out_name : tmp_name, I4_WRITE);
  if (!out)
    return 0;

  if (uninstall_script)
    uninstall_script->printf("(delete \"%s\")\n", forward_slash(out_name));


  fix_path(fn);
  i4_file_class *in=i4_open(fn);
  if (!in)
  {
    delete out;
    return 0;
  }

  i4_sound_info fmt;
  write_header(out, fmt);

  char stat[200];
  sprintf(stat, "Decoding MP3 : %s", fn);
  i4_status_class *status=i4_create_status(stat);

  i4_bool ret=i4_load_mp3(in, out, fmt, status);
  out->seek(0);
  write_header(out, fmt);
    
  delete status;
  delete in;
  delete out;

  if (resample_rate)
  {
    down_sample_wav(tmp_name, out_name, resample_rate);
    i4_unlink(tmp_name);
  }


  if (!ret)
    return 0;

  return li_true_sym; 
}




li_object *copy_file(li_object *o, li_environment *env)
{
  char *f1=li_get_string(li_eval(li_first(o,env),env),env);
  char *f2=li_get_string(li_eval(li_second(o,env),env),env);
  char full_f2[256];
  sprintf(full_f2, "%s/%s", inst_path, f2);

  fix_path(full_f2);
  i4_file_class *out=i4_open(full_f2, I4_WRITE | I4_NO_BUFFER);
  if (!out)
  {
    error_win->printf("Could not open output file %s\n", f2);
    return 0;
  }

  if (uninstall_script)
    uninstall_script->printf("(delete \"%s\")\n", forward_slash(full_f2));

  fix_path(f1);
  i4_file_class *in=i4_open(f1, I4_NO_BUFFER | I4_READ);
  if (!in)
  {
    error_win->printf("Could not open %s\nAre you running setup from the correct directory?", f1);
    return 0;
  }

  char s[200];
  sprintf(s, "Copying to %s", f2);
  i4_status_class *status=i4_create_status(s);


  char buf[4096];
  int tot=in->size(),size=0;

  while (sizeupdate((size+2)/(float)(tot+1));

    int t=in->read(buf, sizeof(buf));
    if (out->write(buf, t)!=t)
    {
      error_win->printf("Could write, out of disk space?\n");
      return 0;
    }

    size+=t;
  }
  delete status;

  delete in;
  delete out;
  return li_true_sym;
}


i4_bool install(int install_type)
{
  char display[200];
  i4_text_item_class *ti=(i4_text_item_class *)display_picker->get_current_item();
  i4_os_string(ti->get_text(), display, 200);

#ifdef _WINDOWS
    MakeShortcut();
#endif
  

  if (!installed)
  {

    i4_os_string(*(path->get_edit_string()), inst_path, 200);

    i4_file_status_struct r_stat;

    int l=strlen(inst_path);

    while (l && (inst_path[l-1]==' ' ||
                 inst_path[l-1]=='\\' ||
                 inst_path[l-1]=='/'))
    {
      inst_path[l-1]=0;
      l--;
    }

    // try to make the installation path
    if (!i4_mkdir(inst_path))
    {
      if (!i4_get_status(inst_path, r_stat))
      {
        error_win->printf("Could not create Directory '%s'\n", inst_path);
        return i4_F;
      }
      else if ((r_stat.flags & I4_FILE_STATUS_DIRECTORY)==0)
      {
        error_win->printf("%s is a FILE not a Directory!\n", inst_path);
        return i4_F;
      }
    }

    char uscript[256];
    sprintf(uscript, "%s/uninstall.scm", inst_path);
    uninstall_script=i4_open(uscript, I4_WRITE);
    if (!uninstall_script)
    {
      error_win->printf("Could not open %s for writing\n", uscript);
      return i4_F;
    }


    char cd_dest[200];
    sprintf(cd_dest,"%s/golgotha.cd",inst_path);


    if (copy_file(li_make_list(new li_string("golgotha.cd"), 
                               new li_string("golgotha.cd"),0),0)==0)
    {
      delete uninstall_script; uninstall_script=0;
      return i4_F;
    }

    g1_set_cd_image(cd_dest);



    i4_status_class *s=i4_create_status("Decompressing Files");
    li_load("install.scm", 0, s);
    delete s;

    delete uninstall_script; 
    uninstall_script=0;


  // check for enough free space
    w32 needed_size=inst_sizes[size_picker->get_current()]*1024*1024;
  
  
//     w32 free_space=i4_disk_free_space(inst_path);
//     if (free_spaceprintf("Free space = %dMB, I need %dMB to install!\n"
//                         "Please Remove some files or select smaller install\n",
//                         free_space/(1024*1024), needed_size/(1024*1024));
//       return i4_F;
//     }    
  }


  char tmp_data[256];

  // don't set sfx path if it's already set
  if (install_type==INSTALL ||
      !i4_get_registry(I4_REGISTRY_USER, sfx_key, "G_SFX_PATH", tmp_data, 256))
  {
    if (!i4_set_registry(I4_REGISTRY_USER, sfx_key, "G_SFX_PATH", "sfx"))
      error_win->printf("Could not set Registry item\n");
  }

  // don't set install path if it's already set
  if (install_type==INSTALL ||
      !i4_get_registry(I4_REGISTRY_USER, reg_key, "install_path", tmp_data, 256))
  {
    if (!i4_set_registry(I4_REGISTRY_USER, reg_key, "install_path", inst_path))
      error_win->printf("Could not set Registry item\n");
  }

  // always set the display they picked
  if (!i4_set_registry(I4_REGISTRY_MACHINE, i4_display_key, "display", display))
  {
    error_win->printf("Could not set Registry item\n");
    return i4_F;
  }
     
  if (install_type==INSTALL || install_type==SAVE_AND_RUN)
  {
    i4_chdir(inst_path);
    system("golgotha");
  }
 
  return i4_T;

}

class setup_app : public i4_application_class
{
public:

  


  void add_display_picker(int x, int y, i4_parent_window_class *p)
  {
    i4_text_window_class *ti=new i4_text_window_class("Rendering Method", style);
    p->add_child(x,y, ti);


    x+=col2;

    i4_list_box_class *lbox=new i4_list_box_class(250, style, wm);

    char old_name[256];
    if (!i4_get_registry(I4_REGISTRY_MACHINE, i4_display_key, "display", old_name, 256))
      old_name[0]=0;

    int i=0, use=0;
    for (i4_display_list_struct *f=i4_display_list; f; f=f->next) 
    {
      
      if (strcmp(f->name, "Windowed GDI") &&  // these displays don't work with render/
          strcmp(f->name, "X Windows"))
      {
        if (strcmp(f->name, old_name)==0)
          use=i;

        i4_str *s=new i4_str(f->name);
        lbox->add_item(new i4_text_item_class(*s, style, 0,0, 
                                              new i4_event_reaction_class(this,DISPLAY_SELECT + i)));      
        i++;
      }        
    }

    if (i==0)
      i4_error("Golgotha requires DirectX5, or a 3dfx card\n"
               "Make sure you have DX5 (and are in 16bit color mode)"
               "Or that you have the current version Glide (for 3dfx) installed");

    lbox->set_current_item(use);
    display_picker=lbox;


    p->add_child(x,y, lbox);
  }

  
  void add_install_path(int x, int y, i4_parent_window_class *p)
  {
    p->add_child(x,y, new i4_text_window_class("Install Path", style));
    x+=col2;

    path=new i4_text_input_class(style, "c:\\golg", 200, 256, this);
    p->add_child(x,y,path);

    x+=path->width();
    
//     i4_button_class *but;
//     but=new i4_button_class(0, new i4_text_window_class("Browse", style), style,
//                                           new i4_event_reaction_class(this, BROWSE));
//     but->set_popup(i4_T);
//     p->add_child(x,y, but);
                                          

  }


  void add_texture_size_picker(int x, int y, i4_parent_window_class *p)
  {
    i4_text_window_class *ti=new i4_text_window_class("Install size", style);
    p->add_child(x,y, ti);
    x+=col2;

    i4_list_box_class *lbox=new i4_list_box_class(250, style, wm);
    lbox->add_item(new i4_text_item_class("75MB (Compact)", style, 0,0, 
                                          new i4_event_reaction_class(this, COMPACT)));


    lbox->add_item(new i4_text_item_class("100MB (Typical)", style, 0,0,
                                          new i4_event_reaction_class(this, TYPICAL)));

    size_needed_to_install=60*1024*1024;
    lbox->set_current_item(1);
    p->add_child(x,y, lbox);
    size_picker=lbox;
    

  }

  void add_buttons(int x, int y, i4_parent_window_class *p)
  {
    i4_button_class *b;
    if (installed)
    {
      b=new i4_button_class(0, new i4_text_window_class("Save & Quit", style), style,
                            new i4_event_reaction_class(this, SAVE_AND_QUIT));
      b->set_popup(i4_T);
      p->add_child(x,y,b);
      x+=b->width();

      b=new i4_button_class(0, new i4_text_window_class("Save & Run", style), style,
                            new i4_event_reaction_class(this, SAVE_AND_RUN));
    }
    else
      b=new i4_button_class(0, new i4_text_window_class("Install & Run", style), style,
                            new i4_event_reaction_class(this, INSTALL));
    b->set_popup(i4_T);
    p->add_child(x,y,b);


    x+=b->width();
    b=new i4_button_class(0, new i4_text_window_class("      Exit     ", style), style,
                          new i4_event_reaction_class(this, QUIT));
    p->add_child(x,y,b);
    x+=b->width();


    if (installed)
    {
      b=new i4_button_class(0, new i4_text_window_class("    Uninstall    ", style), style,
                            new i4_event_reaction_class(this, UNINSTALL));
      p->add_child(x,y,b);
    }


  }

  i4_bool check_installed()
  {
    for (int i=1; ifont_hint->normal_font=new i4_anti_proportional_font_class(i4_load_image("setup_font.tga"));
    style->icon_hint->background_bitmap=i4_load_image("setup.jpg");

    style->color_hint->text_foreground=0xffffff;
    style->color_hint->text_background=0;

    i4_image_window_class *im=new i4_image_window_class(style->icon_hint->background_bitmap,
                                                        i4_T, i4_F);


    wm->add_child(0,0, im);

    i4_deco_window_class *deco=new i4_deco_window_class(400, 100, i4_F, style);
    int x=120, y=110;

    im->add_child(x, y, deco); 
    y+=deco->height()+20;

    i4_graphical_style_class *style=wm->get_style();


    add_display_picker(10,10, deco);

    if (!check_installed())
    {
      add_install_path(10, 40, deco);

      add_texture_size_picker(10,70, deco);
    
      setup_window=new setup_options(380, 80, style);
      deco=new i4_deco_window_class(400, 100, i4_F, style);
      deco->add_child(deco->get_x1(), deco->get_y1(), setup_window);
      wm->add_child(x,y, deco);
      y+=deco->height()+5;
    }

    error_win=new i4_text_scroll_window_class(style, 0xff0000, style->color_hint->neutral(), 
                                              400, 30);
    wm->add_child(x, y, error_win);
    y+=error_win->height()+10;

    add_buttons(x, y, wm);

  }

  void receive_event(i4_event *ev)
  {
    if (ev->type()==i4_event::OBJECT_MESSAGE)
      setup_changed();
    else if (ev->type()==i4_event::USER_MESSAGE)
    {

      CAST_PTR(uev, i4_user_message_event_class, ev);
      switch (uev->sub_type)
      {
        case QUIT :
          quit();
          break;
          
        case UNINSTALL :
        {
          char uscript[256];
          sprintf(uscript, "%s/uninstall.scm", inst_path);
          i4_status_class *stat=i4_create_status("Uninstalling");
          li_load(uscript,0,stat);
          delete stat;
          quit();
        } break;

//         case CHECK_SPACE_BEFORE_INSTALL :
//         {
//           if (i4_disk_free_space(inst_path)add_child(

        //        } break;

        case SAVE_AND_QUIT :
        case SAVE_AND_RUN :
        case INSTALL :
        {
          i4_image_class *im=i4_load_image("thanks.jpg");
          i4_image_window_class *iwin=new i4_image_window_class(im, i4_T, i4_F);
          wm->add_child(0,0,iwin);
          
          if (install(uev->sub_type))
            quit();


          delete iwin;


        } break;

        case BROWSE :
        {
            
          char p[200];
          i4_os_string(*(path->get_edit_string()), p, 200);

          i4_create_file_save_dialog(style, 
                                     p,
                                     "Installation Path",
                                     p,
                                     "*",
                                     "Directory",
                                     this, BROWSE_OK,
                                     BROWSE_CANCEL);



        } break;

        case BROWSE_OK :
        {
          CAST_PTR(fev, i4_file_open_message_class, ev);
          path->change_text(*fev->filename);

          setup_changed();
        } break;

        default:
          setup_changed();
          break;

      }
    }
    else i4_application_class::receive_event(ev);
  }

  
  i4_bool get_display_name(char *name, int max_len)
  {
    strcpy(name, "Windowed GDI");
    return i4_T;
  }

  char *name() { return "test_app"; }
};

void i4_main(w32 argc, i4_const_str *argv)
{
  setup_app test;
  test.run();
}