#import "Controller.h"
#import "EnhancedText.h"
#import "Defaults.h"
#import "TokenString.h"
#import "Subprocess.h"
#import "Boolean.h"
#import "UpDownView.h"
#import "EnhancedMatrix.h"

#define STRING_BUF_SIZE 100

void tickTock(DPSTimedEntry te, double now, void *userData );

@implementation Controller

- init
{
   _defaults = [Defaults new];
   _logProcess = nil;
   _sound = [[Sound alloc] init];
   _currentHostName = [[StringStorage alloc] init];
   _totalTimeDWrite = [[StringStorage alloc] init];
   _startDWrite = [[StringStorage alloc] init];
   _lastResetDWrite = [[StringStorage alloc] init];
   return self;
}

- awakeFromNib
{
   id myConnection;

   [self setCurrentHostName: [_defaults get: "DefaultHost"]];
					     /* Remote object connection */
   _connectionStatus = [[Boolean alloc] init];
   [_connectionStatus setDelegate: self];
   myConnection = [NXConnection registerRoot: _connectionStatus withName: [NXApp appName]];
   [myConnection runFromAppKit];

   [self updateCounters: self];
   [upDownView setIsUp: NO];

   if(!strcmp("Yes",[_defaults get: "DisplayLog"]))
       [self openLog: self];

   if(!strcmp("Yes",[_defaults get: "DisplayMeter"]))
       [meterPanel makeKeyAndOrderFront: self];

   [self initUpStatus];			     /* Are we alreay up? */
   
   /* Do we want to autolaunch ppp... try it if we do - won't hurt. */
   if(!strcmp("Yes",[_defaults get: "StartPPPOnLaunch"]))
       [self start: self];

   return self;
}

- updateCounters: sender
{
   char tbuf[64];
   time_t upTime, newTotal;

   [[total window] setTitle: [self currentHostName]];
   [upDownView display];
   upTime = [upDownView upTime];
   newTotal = totalTime + upTime;
   if(!(newTotal % 60) || (sender == nil))
   {
      sprintf(tbuf,"%ld",newTotal);
      [_defaults writeDB: [self totalTimeDWrite] as: tbuf];
   }
      
   sprintf(tbuf,"%d:%02d:%02d", newTotal / 3600 , (newTotal % 3600) / 60, newTotal % 60);
   [total setStringValue: tbuf];
   sprintf(tbuf,"%d:%02d:%02d", upTime / 3600 , (upTime % 3600) / 60, upTime % 60);
   [session setStringValue: tbuf];

   return self;
}


- resetCounters: sender
{
   totalTime = 0L;
   [upDownView resetUpTime];
   [self setLastResetDate: time((time_t *)0)];
   return [self updateCounters: sender];
}

- startDefault: sender
{
   [self setCurrentHostName: [_defaults get: "DefaultHost"]];
   [self start: sender];
   return self;
}


- start: sender
{
   id proc;

   if(![_connectionStatus boolState])
       proc = [[Subprocess alloc] init: [_defaults get: [self startDWrite]] withDelegate: nil
	   andPtySupport: YES andStdErr: NO];

   return self;
}

- stop: sender
{
   id proc;

   if([_connectionStatus boolState])
       proc = [[Subprocess alloc] init: [_defaults get: "PPPStop"]];

   return self;
}

- openLog:sender
{
   if(!_logProcess)
   {
      id cmd = [[[StringStorage alloc] init] autorelease];

      [cmd setFStringValue: "tail -1f %s",[_defaults get: "LogFile"]];
      _logProcess = [[Subprocess alloc] init: [cmd stringValue] withDelegate: self 
		 andPtySupport: NO andStdErr: NO];
   }
   [logPanel makeKeyAndOrderFront: self];
   return self;
}


- (void) setLastResetDate: (time_t) atime
{
   id string = [[[StringStorage alloc] init] autorelease];
   char *dateStr;

   if(!atime)
       atime = (time_t)atol(((dateStr = (char *)[_defaults get: [self lastResetDWrite]])?dateStr:"0"));
   else
   {
      [string setFStringValue: "%ld", (long) atime];
      [_defaults writeDB: [self lastResetDWrite] as: [string stringValue]];
   }
   
   dateStr = ctime(&atime);
   [string setFStringValue: "%6.6s %4.4s", dateStr + 4, dateStr + 20];
   [lastReset setStringValue: [string stringValue]];
}

- (void) setIsUp: (BOOL) aValue since: (time_t) aTime
{
   if(aValue)
       [upDownView setIsUp: aValue since: aTime];

   [_connectionStatus setBoolState: aValue];
}

- (void) initUpStatus
{
   id str;
   FILE *proc;
   char buf[STRING_BUF_SIZE + 1], *ptr;
   BOOL isUp = NO;
   time_t since = (time_t)0, now;

   now = time((time_t *)0);
   					     /* See if ppp0 is up already.... */
   if(proc = popen("/usr/etc/ifconfig ppp0 | fgrep UP","r"))
   {
      if(fgets(buf,STRING_BUF_SIZE,proc) && strstr(buf,"UP"))
      {					     /* it is... since when? */
	 isUp = YES;
	 pclose(proc);

					     /* look for the uptime message pppstatus places in */
	 str = [[[StringStorage alloc] init] autorelease];
	 [str setFStringValue: "/usr/bin/fgrep 'PPPMeter uptime:' %s | /usr/ucb/tail -1",
	  [_defaults get: "LogFile"]];

	 if(proc = popen([str stringValue],"r"))
	 {
	    if(fgets(buf,STRING_BUF_SIZE,proc) && (ptr = strstr(buf,"uptime:")))
	    {				     /* found it! */
	       sscanf(ptr,"uptime:%ld",&since);
	       [str setFStringValue: 
		"PPP is up! I found an uptime of %14.14s - use that or NOW?", ctime(&since) + 4];
	       if(NXRunAlertPanel([NXApp appName], [str stringValue], "Use it", "Use NOW", NULL) == 
		  NX_ALERTALTERNATE)
		   since = now;
	    }
	    else
	    {
	       NXRunAlertPanel([NXApp appName], "PPP is up! I can't tell from when - starting meter NOW.",
			       "OK", NULL, NULL);
	       since = now;
	    }
	 }
      }
      pclose(proc);
   }

   [self setIsUp: isUp since: since];
}

- (const char *) currentHostName
{
   return [_currentHostName stringValue];
}

- (void) setCurrentHostName: (const char *)aHost
{
   const char *str;

   [_currentHostName setStringValue: aHost];
   [_totalTimeDWrite setFStringValue: "%s TotalTime", aHost];
   [_startDWrite setFStringValue: "%s PPPStart", aHost];
   [_lastResetDWrite setFStringValue: "%s LastReset", aHost];
   totalTime = atol(((str = [_defaults get: [self totalTimeDWrite]])?str:"0"));
   [self setLastResetDate: (time_t)0];
   [upDownView resetUpTime];
   [self updateCounters: self];
}

- (const char *) totalTimeDWrite
{
   return [_totalTimeDWrite stringValue];
}

- (const char *) startDWrite
{
   return [_startDWrite stringValue];
}

- (const char *) lastResetDWrite
{
   return [_lastResetDWrite stringValue];
}

- startHost: sender
{
   [hosts loadFromString: [_defaults get: "Hosts"]];
   [hosts setTarget:self];
   [hosts setDoubleAction: @selector(setHost:)];
   [[hosts window] makeKeyAndOrderFront: sender];
   return self;
}

- setHost: sender
{
   [self setCurrentHostName: [hosts selectedStringValue]];
   [[hosts window] orderOut:sender];
   [self start: sender];
   return self;
}

- showStats: sender
{
   id hostList = [[[TokenString alloc] init: [_defaults get: "Hosts"]] autorelease];
   id str = [[[StringStorage alloc] init] autorelease];
   const char *label, *ptr;
   time_t uTime;

   [hostList setSeparator: ';'];
   [stats empty: self];
   while((label = [hostList popStringValue]))
   {
      [stats appendStringValue: label];

      [str setFStringValue: "%s TotalTime",label];
      uTime = atol((ptr = [_defaults get: [str stringValue]])? ptr : "0");
      [str setFStringValue: "%d:%02d:%02d", uTime/3600,(uTime % 3600)/60,uTime%60];
      [stats appendStringValue: " has "];
      [stats appendStringValue: [str stringValue]];
      [stats appendStringValue: " connect time since "];

      [str setFStringValue: "%s LastReset",label];
      uTime = atol((ptr = [_defaults get: [str stringValue]])? ptr : "0");
      ptr = ctime(&uTime);
      [str setFStringValue: "%6.6s %4.4s", ptr + 4, ptr + 20];

      [stats appendStringValue: [str stringValue]];
      [stats appendStringValue: "\n"];
   }
   [[stats window] makeKeyAndOrderFront: self];
   return self;
}


@end


@implementation Controller (ControllerAppDelegate)

- appDidInit: sender
{
   [[[NXApp appIcon] contentView] addSubview: upDownView];
   [[NXApp appIcon] display];
   return self;
}

- appWillTerminate: sender
{
   char tbuf[60];

   if(_logProcess)
       [_logProcess terminate: self];

   if([_connectionStatus boolState])
       [self stop: self];

   sprintf(tbuf,"%ld",totalTime + [upDownView upTime]);
   [_defaults writeDB: [self totalTimeDWrite] as: tbuf];

   return self;
}

@end


@implementation Controller (ControllerSubprocessDelegate)

- subprocessDone
{
   if(_logProcess)
       _logProcess = [_logProcess free];
   return self;
}


- subprocessOutput:(char *)buf
{
   [logText appendStringValue: buf];
   return self;
}

@end

@implementation Controller (ControllerBooleanDelegate)

- (void) boolStateSetTo: (BOOL) aValue
{
   const char *sndFile;

   if(aValue)
   {
      if(![upDownView isUp])		     /* just started... */
      {
	 [upDownView setIsUp: YES];	     /* start new one */
      
	 sndFile = [_defaults get: "UpSound"];
	 if(sndFile && *sndFile)
	 {
	    [_sound readSoundfile: sndFile];
	    [_sound play];
	 }
      }

      ticker = DPSAddTimedEntry(1, tickTock, (void *)self, NX_MODALRESPTHRESHOLD + 1);
   }
   else if (!aValue && [upDownView isUp])
   {
      DPSRemoveTimedEntry(ticker);
      [self updateCounters: nil];	     /* pass nil to force it to write out value */
      [upDownView setIsUp: NO];
      sndFile = [_defaults get: "DownSound"];
      if(sndFile && *sndFile)
      {
	 [_sound readSoundfile: sndFile];
	 [_sound play];
      }
   }

   [upDownView display];
}


@end

void tickTock(DPSTimedEntry te, double now, void *userData )
{
   [(id)userData updateCounters: userData];
}








