
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "wx.h"
#pragma hdrstop

#include "config.h"
#include "song.h"
#include "trackwin.h"
#include "filter.h"

#include "mstdfile.h"
#include "player.h"
#include "jazz.h"
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>

#ifdef VMS
#if __VMS_VER < 70000000
extern "C" char *strdup(const char *s1);
extern "C" int   unlink(const char *file);
#endif
#endif

#include "version.h"

#ifdef wx_msw
#include "winplay.h"
#endif

// -------------------------- global config -----------------------------------

tNamedValue *DrumNames;
tNamedValue *VoiceNames;
tNamedValue *ControlNames;
int Seq2Device;
int Seq2Driver;
int SoftThru;
int HardThru;
int ClockSource;
int RealTimeOut;
int UseReverbMacro;
int UseChorusMacro;
int DrumChannel;
int BankControlNumber;
int MouseButtons;
int PartsColumnsMax;
int PartsTracknamesRight;

int TrackWinGeo[4];
int PianoWinGeo[4];
int PartsDlgGeo[2];
int TrackDlgGeo[2];
int HarmonyGeo[2];
int RhythmGeo[2];

char *StartUpSong;
#ifndef VMS
wxHelpInstance *HelpInstance = NULL;
wxPathList HelpPathList;
#endif

static void InitConfig(void)
{
  int i;

  DrumChannel = 10;
  Seq2Device  = 1;	// my midi device
  Seq2Driver  = 1;	// use /dev/sequencer2
  SoftThru = 1;		// emulate midi thru
  HardThru  = 1;	// mpu401 hardware midi thru
  ClockSource = 0;	// midi clock source (0 = internal)
  RealTimeOut = 0;	// send realtime midi messages to midi out
  UseReverbMacro = 1;	// use GS reverb macro
  UseChorusMacro = 1;	// use GS chorus macro
  MouseButtons = 2;     // 2 button mouse
  BankControlNumber = 0;// Controller for bank select
  PartsColumnsMax = 4;  // Number of columns to draw in Parts dialogs
  PartsTracknamesRight = 1; // Draw tracknames on the right too?
  TrackWinGeo[0] = 10;
  TrackWinGeo[1] = 10;
  TrackWinGeo[2] = 600;
  TrackWinGeo[3] = 400;
  PianoWinGeo[0] = 30;
  PianoWinGeo[1] = 30;
  PianoWinGeo[2] = 600;
  PianoWinGeo[3] = 400;
  PartsDlgGeo[0] = 50;
  PartsDlgGeo[1] = 50;
  TrackDlgGeo[0] = 50;
  TrackDlgGeo[1] = 50;
  HarmonyGeo[0] = 100;
  HarmonyGeo[1] = 100;
  RhythmGeo[0] = 150;
  RhythmGeo[1] = 150;

  DrumNames = new tNamedValue [130];
  for (i = 0; i < 129; i++)
  {
    DrumNames[i].Value = i;
    DrumNames[i].Name  = "";
  }
  DrumNames[129].Name = 0;

  VoiceNames = new tNamedValue [319];
  for (i = 0; i < 318; i++)
  {
    VoiceNames[i].Value = i;
    VoiceNames[i].Name  = "";
  }
  VoiceNames[0].Name = "None";
  VoiceNames[318].Name = 0;

  ControlNames = new tNamedValue [130];
  for (i = 0; i < 129; i++)
  {
    ControlNames[i].Value = i;
    ControlNames[i].Name  = "";
  }
  ControlNames[129].Name = 0;
}



static void LoadConfig(FILE *fd)
{
  char buf[1000];
  int i, j, voice_index = 0;
  tNamedValue *nv = 0;
  while (fgets(buf, sizeof(buf), fd) != NULL)
  {
    if (strncmp(buf, ".drumchannel", 12) == 0)
      sscanf(buf, ".drumchannel %d", &DrumChannel);
    else if (strncmp(buf, ".mouse", 6) == 0)
      sscanf(buf, ".mouse %d", &MouseButtons);
    else if (strncmp(buf, ".device", 7) == 0)
      sscanf(buf, ".device %d", &Seq2Device);
    else if (strncmp(buf, ".driver", 7) == 0)
      sscanf(buf, ".driver %d", &Seq2Driver);
    else if (strncmp(buf, ".softthru", 9) == 0)
      sscanf(buf, ".softthru %d", &SoftThru);
    else if (strncmp(buf, ".hardthru", 9) == 0)
      sscanf(buf, ".hardthru %d", &HardThru);
    else if (strncmp(buf, ".realtime_out", 13) == 0)
      sscanf(buf, ".realtime_out %d", &RealTimeOut);
    else if (strncmp(buf, ".clocksource", 12) == 0)
      sscanf(buf, ".clocksource %d", &ClockSource);
    else if (strncmp(buf, ".use_reverb_macro", 17) == 0)
      sscanf(buf, ".use_reverb_macro %d", &UseReverbMacro);
    else if (strncmp(buf, ".use_chorus_macro", 17) == 0)
      sscanf(buf, ".use_chorus_macro %d", &UseChorusMacro);
    else if (strncmp(buf, ".bank_control_number", 20) == 0)
      sscanf(buf, ".bank_control_number %d", &BankControlNumber);
    else if (strncmp(buf, ".parts_columns_max", 18) == 0)
      sscanf(buf, ".parts_columns_max %d", &PartsColumnsMax);
    else if (strncmp(buf, ".parts_tracknames_right", 23) == 0)
      sscanf(buf, ".parts_tracknames_right %d", &PartsTracknamesRight);
    else if (strncmp(buf, ".trackwin_xpos", 14) == 0)
      sscanf(buf, ".trackwin_xpos %d", &TrackWinGeo[0]);
    else if (strncmp(buf, ".trackwin_ypos", 14) == 0)
      sscanf(buf, ".trackwin_ypos %d", &TrackWinGeo[1]);
    else if (strncmp(buf, ".trackwin_width", 15) == 0)
      sscanf(buf, ".trackwin_width %d", &TrackWinGeo[2]);
    else if (strncmp(buf, ".trackwin_height", 16) == 0)
      sscanf(buf, ".trackwin_height %d", &TrackWinGeo[3]);
    else if (strncmp(buf, ".pianowin_xpos", 14) == 0)
      sscanf(buf, ".pianowin_xpos %d", &PianoWinGeo[0]);
    else if (strncmp(buf, ".pianowin_ypos", 14) == 0)
      sscanf(buf, ".pianowin_ypos %d", &PianoWinGeo[1]);
    else if (strncmp(buf, ".pianowin_width", 15) == 0)
      sscanf(buf, ".pianowin_width %d", &PianoWinGeo[2]);
    else if (strncmp(buf, ".pianowin_height", 16) == 0)
      sscanf(buf, ".pianowin_height %d", &PianoWinGeo[3]);

    else if (strncmp(buf, ".partsdialog_xpos", 17) == 0)
      sscanf(buf, ".partsdialog_xpos %d", &PartsDlgGeo[0]);
    else if (strncmp(buf, ".partsdialog_ypos", 17) == 0)
      sscanf(buf, ".partsdialog_ypos %d", &PartsDlgGeo[1]);

    else if (strncmp(buf, ".trackdialog_xpos", 17) == 0)
      sscanf(buf, ".trackdialog_xpos %d", &TrackDlgGeo[0]);
    else if (strncmp(buf, ".trackdialog_ypos", 17) == 0)
      sscanf(buf, ".trackdialog_ypos %d", &TrackDlgGeo[1]);

    else if (strncmp(buf, ".harmonybrowser_xpos", 20) == 0)
      sscanf(buf, ".harmonybrowser_xpos %d", &HarmonyGeo[0]);
    else if (strncmp(buf, ".harmonybrowser_ypos", 20) == 0)
      sscanf(buf, ".harmonybrowser_ypos %d", &HarmonyGeo[1]);
    else if (strncmp(buf, ".randomrhythm_xpos", 18) == 0)
      sscanf(buf, ".randomrhythm_xpos %d", &RhythmGeo[0]);
    else if (strncmp(buf, ".randomrhythm_ypos", 18) == 0)
      sscanf(buf, ".randomrhythm_ypos %d", &RhythmGeo[1]);
    else if (strncmp(buf, ".drumnames", 10) == 0)
      nv = DrumNames;
    else if (strncmp(buf, ".voicenames", 11) == 0)
      	nv = VoiceNames;
    else if (strncmp(buf, ".ctrlnames", 10) == 0)
      nv = ControlNames;
    else if (nv && isdigit(buf[0]))
    {
	if (nv == VoiceNames) {
		assert( 0 <= voice_index && voice_index <= 317);
      		sscanf(buf, " %d %n", &i, &j);
      		assert(0 <= i && i <= 32639);
      		buf[ strlen(buf)-1 ] = 0;	// cut off \n
		nv[voice_index+1].Value = i + 1;
      		nv[voice_index+1].Name = strdup(buf + j);
		voice_index++;
	}
	else {
      		sscanf(buf, " %d %n", &i, &j);
      		assert(0 <= i && i <= 127);
      		buf[ strlen(buf)-1 ] = 0;	// cut off \n
      		nv[i+1].Name = strdup(buf + j);
	}
    }
  }
}



#if 0
Bool wxFileExists(const char *fname)
{
  FILE *fd = fopen(fname, "r");
  if (fd)
  {
    fclose(fd);
    return TRUE;
  }
  return FALSE;
}
#endif


static const char *ConfigFile()
{
  static char buf[256];

#ifdef wx_msw
  const char *fname = "jazz.cfg";
#else
#ifndef VMS
  const char *fname = ".jazz";
#else
  const char *fname = "jazz.cfg";
#endif
#endif

  if (wxFileExists((char *)fname))
    return fname;

  char *home;
  if ((home = getenv("HOME")) != 0)
  {
#ifndef VMS
    sprintf(buf, "%s/%s", home, fname);
#else
    sprintf(buf, "%s%s", home, fname);
#endif
    if (wxFileExists(buf))
      return buf;
  }

  // look where the executable was started
  home = wxPathOnly((char *)wxTheApp->argv[0]);
#ifndef VMS
  sprintf(buf, "%s/%s", home, fname);
#else
  sprintf(buf, "%s%s", home, fname);
#endif
  if (wxFileExists(buf))
    return buf;

  return 0;
}



Bool GetConfig(const char *entry, char *value)
{
  const char *fname = ConfigFile();
  if (!fname)
    return FALSE;

  FILE *fd = fopen(fname, "r");
  int  len = strlen(entry);
  char buf[1000];
  Bool found = FALSE;
  while (!found && fgets(buf, sizeof(buf), fd) != NULL)
  {
    if (strncmp(buf, entry, len) == 0)
    {
      strcpy(value, buf + len);
      found = TRUE;
    }
  }
  fclose(fd);
  return found;
}


Bool PutConfig(const char *entry, const char *value)
{
  const char *fname = ConfigFile();
  if (!fname)
    return FALSE;

  FILE *out = fopen("jazz-cfg.tmp", "w");
  if (!out)
    return FALSE;

  FILE *inp = fopen(fname, "r");
  int  len = strlen(entry);
  char buf[1000];
  Bool found = FALSE;
  while (fgets(buf, sizeof(buf), inp) != NULL)
  {
    if (strncmp(buf, entry, len) == 0)
    {
      fprintf(out, "%s %s\n", entry, value);
      found = TRUE;
    }
    else
      fputs(buf, out);
  }
  if (!found)
    fprintf(out, "%s %s\n", entry, value);
  fclose(inp);
  fclose(out);
  unlink(fname);
  rename("jazz-cfg.tmp", fname);
  return TRUE;
}


Bool PutConfig(const char *entry, long value)
{
  char buf[50];
  sprintf(buf, "%ld", value);
  return PutConfig(entry, buf);
}

Bool GetConfig(const char *entry, long &value)
{
  char buf[512];
  if (GetConfig(entry, buf))
  {
    sscanf(buf, " %ld ", &value);
    return TRUE;
  }
  return FALSE;
}


// --------------------------- jazz application -------------------------------


class tApp: public wxApp
{
  public:
    wxFrame *OnInit(void);
};


// This statement initialises the whole application
tApp     myApp;



wxFrame *tApp::OnInit(void)
{

#ifndef wx_msw
  if (GetArgOpt("-h") || GetArgOpt("-v")) {
	char *myname = wxTheApp->argv[0];
	printf("\n%s\n", about_text );
	printf("\n\nOptions (in any order): \n");
	printf("%s -h                                   -- Print this text\n", myname );
	printf("%s -v                                   -- Print this text\n", myname );
	printf("%s -f filename                          -- Input midi file \n", myname );
	printf("%s -trackwin xpos ypos [width height]   -- Position/size of main window\n", myname );
	printf("%s -pianowin xpos ypos [width height]   -- Position/size of piano roll window\n", myname );
	exit(0);
  }
#endif

  InitConfig();

  const char *fname = ConfigFile();
  if (fname)
  {
    FILE *fd = fopen(fname, "r");
    LoadConfig(fd);
    fclose(fd);
  }

#ifndef VMS
  HelpPathList.AddEnvList("WXHELPFILES");
  HelpPathList.AddEnvList("PATH");
  HelpInstance = new wxHelpInstance(TRUE);
  HelpInstance->Initialize("jazz");
#endif

  tSong *Song = new tSong;
  wxFrame *frame = new wxFrame; 
  Midi = new tNullPlayer(Song);

#ifndef wx_msw
  if (Seq2Driver)
  {
#ifdef DEV_SEQUENCER2
    Midi = new tSeq2Player(Song);
    if (!Midi->Installed())
    {
      perror("/dev/sequencer2");
      fprintf(stderr, "Jazz will start with no play/record ability\n");
      Midi = new tNullPlayer(Song);
    }
#else
    fprintf(stderr, "this version was compiled without support for /dev/sequencer2\n");
#endif
  }
  else
  {
#ifdef DEV_MPU401
    Midi = new tMpuPlayer(Song);
    if (!Midi->Installed())
    {
      fprintf(stderr, "Could not connect to midinet server at host '%s'\n", midinethost );
      fprintf(stderr, "Jazz will start with no play/record ability\n");
      Midi = new tNullPlayer(Song);
    }
#else
    fprintf(stderr, "this version was compiled without support for jazz's /dev/mpu401\n");
#endif
  }
#endif // ifndef wx_msw

  int *geo = TrackWinGeo;
  int i;
  int opt;

  opt = GetArgOpt( "-trackwin" ) + 1;
  for (i = 0; i < 4; i++, opt++) {
	if ((wxTheApp->argc > opt) && isdigit( wxTheApp->argv[opt][0] ))
		geo[i] = atoi( wxTheApp->argv[opt] );
	else
		break;
  }


  opt = GetArgOpt( "-f" ) + 1;

  char *path = wxGetWorkingDirectory( NULL );
  char *file;

  if (opt && (wxTheApp->argc > opt))
	file = wxTheApp->argv[opt];
  else
	file = "jazz.mid";

  StartUpSong = new char[ strlen( path ) + 1 + strlen( file ) + 1 ];
  StartUpSong[0] = '\0';
  strcat( StartUpSong, path );
#ifndef VMS
  strcat( StartUpSong, "/" );
#endif
  strcat( StartUpSong, file );

  FILE *fd = fopen(StartUpSong, "r");
  if (fd)
  {
    fclose(fd);
    tStdRead io;
    Song->Read(io, StartUpSong);
    if (strcmp(file, "jazz.mid"))
      lasts = StartUpSong;
  }
//  TrackWin = new tTrackWin(0, "Jazz!", Song, geo[0], geo[1], geo[2], geo[3] );
  TrackWin = new tTrackWin(frame, "Jazz!", Song, geo[0], geo[1], geo[2], geo[3] );
  TrackWin->Create();
  TrackWin->Show(TRUE);
  if (fd)
  {
    TrackWin->SetTitle( lasts );
    TrackWin->Redraw();
  }

#ifdef wx_msw
  // create after Trackwin is there
  switch (ClockSource)
  {
    case CsMidi:
      Midi = new tWinMidiPlayer(Song);
      break;
    case CsMtc:
      Midi = new tWinMtcPlayer(Song);
      break;
    case CsFsk:
    case CsInt:
    default:
      Midi = new tWinIntPlayer(Song);
      break;
  }
  if (!Midi->Installed())
  {
    wxMessageBox("no midi driver installed", "Error");
    Midi = new tNullPlayer(Song);
  }
#endif

  return TrackWin;
}

