/*
	Month Calendar - A Monthly Calendar with Week Numbers
	Copyright © 2005-2009 Harry Whitfield

	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., 51 Franklin St, Fifth Floor, Boston,
	MA  02110-1301  USA
	
	Month Calendar - version 3.4
	28 June, 2009
	Copyright © 2005-2009 Harry Whitfield
	mailto:g6auc@arrl.net
*/

var zenithPref;
var timeZonePref;
var timeZoneOffset;
		
var altitudePref;
var altitudeUnitsPref;
var altitudeMetresMax = 15240;	// 15240 metres
var altitudeFeetMax   = 50000;	// 50000 feet

function bf(s)	// for localization of strings
{
	return widget.getLocalizedString(s);
}

function deleteSelectHotKey()
{
	//delete selectHotKey;
	selectHotKey = null;
}

function makeSelectHotKey(keyDef, action)		// for example: Shift+Control+F8
{
	//eprint('***** makeSelectHotKey *****');
	
	var idx = keyDef.lastIndexOf("+");
	var modifier = keyDef.substring(0,idx);
	var key = keyDef.substring(idx+1);
	if (key == 'Tab') { key = 'Space'; }
	
	if (selectHotKey !== null) { deleteSelectHotKey(); }

	selectHotKey = new HotKey();
	selectHotKey.modifier = modifier;
	eprint('makeSelectHotKey:modifier=' + selectHotKey.modifier);
	selectHotKey.key = key;
	eprint('makeSelectHotKey:key=' + selectHotKey.key);
	//eprint('makeSelectHotKey:action=' + action);

	if (systemPlatform == "macintosh")
	{
		selectHotKey.onKeyUp = action;
		//eprint('makeSelectHotKey:onKeyUp=' + selectHotKey.onKeyUp);
	}
	else
	{
		selectHotKey.onKeyDown = action;
		//eprint('makeSelectHotKey:onKeyDown=' + selectHotKey.onKeyDown);
	}
	
	//eprint('*end* makeSelectHotKey *end*');
}

function deleteSpeechHotKey()
{
	//delete speechHotKey;
	speechHotKey = null;
}

function makeSpeechHotKey(keyDef, action)		// for example: Shift+Control+F8
{
	//eprint('***** makeSpeechHotKey *****');

	var idx = keyDef.lastIndexOf("+");
	var modifier = keyDef.substring(0,idx);
	var key = keyDef.substring(idx+1);
	if (key == 'Tab') { key = 'Space'; }

	if (speechHotKey !== null) { deleteSpeechHotKey(); }

	speechHotKey = new HotKey();
	speechHotKey.modifier = modifier;
	eprint('makeSpeechHotKey:modifier=' + speechHotKey.modifier);
	speechHotKey.key = key;
	eprint('makeSpeechHotKey:key=' + speechHotKey.key);
	//eprint('makeSpeechHotKey:action=' + action);

	if (systemPlatform == "macintosh")
	{
		speechHotKey.onKeyUp = action;
		//eprint('makeSpeechHotKey:onKeyUp=' + speechHotKey.onKeyUp);
	}
	else
	{
		speechHotKey.onKeyDown = action;
		//eprint('makeSpeechHotKey:onKeyDown=' + speechHotKey.onKeyDown);
	}
	
	//eprint('*end* makeSpeechHotKey *end*');

}

function simpleForm(formTitle, button1, name, title, type, option, defaultValue, description)
{
	var result = "";
	
	var formfields = [];

	formfields[0] = new FormField();
	formfields[0].name = name;
	formfields[0].title = title;
	formfields[0].type = type;
	formfields[0].option = option;
	formfields[0].defaultValue = defaultValue;
	formfields[0].description = description;

	var formResults = form(formfields, formTitle, button1);

	if ((formResults !== null) && (formResults !== undefined)) { result = formResults[0]; }
	
	delete formfields[0];
	formfields[0] = null;
	formfields = null;
	return result;
}

function xtn(s)
{
	var ext;
	var idx = s.lastIndexOf(".");
	if (idx >= 0 )
	{
		ext = s.substring(idx);
		if (ext.length > 7) { return ""; }
		if (ext.length > 1) { return ext; }
	}
	return "";
}

function isItemIn(item, vector)
{
	for (var i = 0; i < vector.length; i += 1)
	{
		if (vector[i] == item) { return true; }
	}
	return false;
}

function isText(path)
{
	var result, fileData, item, stype, xtype;
	var textExtns = [".txt",".js",".kon",".c",".bat",".css",".csv",".dtd",".faq",".jav",".js",".log",".pl",".sig",".xml",".htm","html"];

	if (systemPlatform == "macintosh")
	{
		result = getFileInfo(path);
		fileData = result.substr(path.length + 2);
		eprint('isText:Mac fileType=' + fileData);
		item = fileData.split(" ");
		stype = item[0];
		if ((stype == "ASCII") || (stype == "ISO-8859") || (stype == "UTF-8") || (stype == "C++")) { return true; }
	}
	xtype = xtn(path).toLowerCase();
	eprint('isText:Win fileType=' + xtype);
	return isItemIn(xtype, textExtns);
}

function dragDropped()
{
	var path, idx;
	var n = system.event.data.length;
	var dropData = [];
	for (var i = 0; i < n; i += 1) { dropData[i] = system.event.data[i]; }
	var dropType = dropData[0];
	eprint('.... dragDropped ...');
	var text = "";
	if (dropType == "filenames")
	{				
		gSoundFlag = false;
		
		for (i = 1; i < n; i += 1)
		{
			path = dropData[i];
				path = resolvePath(path);	// in case it is an alias
			if (isText(path))
			{
				eprint("dragDropped:Text file: " + path);
				text += filesystem.readFile(path);
			}
			else if (isSound(path))
			{
				eprint("dragDropped:Sound file: " + path);
				eprint("dragDropped:copy: " + filesystem.copy(path, memoFolderPath + '/Alarms'));
				idx = path.lastIndexOf('/');
				gSoundFile = path.substring(idx+1);
				gSoundFlag = true;
			}
			else
			{
				beep();
				eprint('dragDropped: ' + path + ' is not a text or sound file');
				 alert(path + bf('notTextOrSound'));
			}
		}
	}
	else  if (dropType == "string")
	{
		eprint("dragDropped: String accepted.");
		text = dropData[1];
	}
	else if (dropType == "url")
	{
		eprint("dragDropped: URL accepted.");
		text = dropData[1];
	}
	else
	{
		beep();
		eprint("dragDropped: Invalid dropType: " + dropType);
	}
	dropData = null;
	return text;
}

function isSound(path)
{
	var result, fileData, item, stype, mtype, xtype;

	eprint("------ isSound() ------");

	// .aif		IFF data, AIFF audio
	// .aiff	IFF data, AIFF audio
	// .au		Sun/NeXT audio data: 8-bit ISDN u-law, mono, 8000 Hz
	// .mp3		MP3, 128 kBits, 44.1 kHz, Mono
	// .snd		Sun/NeXT audio data: 16-bit linear PCM, mono, 22050 Hz
	// .wav		RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz
	
	var soundExtns = [".aif",".aiff",".au",".mp3",".snd",".wav"];

	if (systemPlatform == "macintosh")
	{
		result = getFileInfo(path);
		fileData = result.substr(path.length + 2);
		eprint('isSound:fileData=' + fileData);
		item = fileData.split(",");
		stype = item[0];
		eprint("isSound:stype=" + stype);
		if ((stype == "IFF data") || (stype == "MP3") ||
		   (stype == "Sun/NeXT audio data: 8-bit ISDN u-law") ||
		   (stype == "Sun/NeXT audio data: 16-bit linear PCM")) { return true; }
		mtype = item[1];
		eprint("isSound:mtype=" + mtype);
		return ((stype == "RIFF (little-endian) data") && (mtype == " WAVE audio"));
	}
	else
	{
		xtype = xtn(path).toLowerCase();
		eprint('isSound:Win fileType=' + xtype);
		return isItemIn(xtype, soundExtns);
	}
}

function openTOiCal()
{
	filesystem.open('Resources/To_iCal.widget');
}

function logMemoFolder()
{
		theString 	 = '\n**************************************************';
		theString 	+= '\nYour memos, copies of iCal(.ics) files, and';
		theString	+= '\ncopies of custom alarm sounds are stored in folder';
		theString	+= '\n' + memoFolderPath;
		theString 	+= '\n**************************************************\n';
		myPrint(theString);
		
		filesystem.writeFile( memoFolderPath + '/ReadMe.txt', theString);
		filesystem.changeMode(memoFolderPath + '/ReadMe.txt', accessMode);
}

function makeBackupDir(path)	// not used
{
	if (filesystem.itemExists(path) && filesystem.isDirectory(path)) { return; }
	eprint('makeBackupDir:mkdir=' + filesystem.createDirectory(path));
}

function backupMemos()
{
	var path = chooseFolder();
	myPrint('backupMemos:path=' + path);
	if (path)
	{
		eprint('backupMemos:copy:' + filesystem.copy(memoFolderPath, path));
	}
}

function get_ang(data, dmax, NE, SW) {	// input angle
	var item, a, d, m, s, t;
	
	eprint("get_ang: " + data + ', ' + dmax + ', ' + NE + ', ' + SW);
	
	item = data.split(' ', 4);		// 49 45 58.2689 N   or  7 33 23.2145 W
	eprint("item: " + item);
	
	if (item.length < 2) { return NaN; }

	a = item[item.length - 1];
	a = a.toUpperCase();
	if ((a != NE) && (a != SW)) { return NaN; }

	d = Number(item[0]); if (isNaN(d)) { return NaN; }
	if ((d < 0) || (d >= dmax)) { return NaN; }
	if (item.length == 2) {
		m = 0;
		s = 0;
	} else {
		if (d != Math.floor(d)) { return NaN; }
		m = Number(item[1]); if (isNaN(m)) { return NaN; }
		if (( m < 0) || (m >= 60)) { return NaN; }
		if (item.length == 3) {
			s = 0;
		} else {
			if (m != Math.floor(m)) { return NaN; }
			s = Number(item[2]); if (isNaN(s)) { return NaN; }
			if ( ( s < 0 ) || ( s >= 60 ) ) { return NaN; }
		}
	}
	
	t = d + m / 60 + s / 3600;
	if ( a == SW ) { t = -t; }

	return t;
}

function dateOf(isoDate)
{
	var len   = isoDate.length;
	var year  = parseInt(isoDate.substring(0,       len - 4), 10);
	var month = parseInt(isoDate.substring(len - 4, len - 2), 10);
	var day   = parseInt(isoDate.substring(len - 2         ), 10);

	return new Date(year, month - 1, day);
}

function zenithTypeOf(zenith)
{		
	if (zenith <  91) { return bf("Official"); }
	if (zenith <  97) { return bf("Civil"); }
	if (zenith < 103) { return bf("Nautical"); }
	if (zenith < 109) { return bf("Astronomical"); }
	return "";
}

function sunRiseSet(isoDate)
{
	var tzOffset;
	var K = preferences.latitudePref.value;		eprint("sunRiseSet:K:  " + K);
	var L = preferences.longitudePref.value;	eprint("sunRiseSet:L:  " + L);
	
	if ((K === "") || (L === "")) { return ""; }

	var latitude  = get_ang(K,  90, "N", "S");	eprint("sunRiseSet:latitude:  " + latitude);
	var longitude = get_ang(L, 180, "E", "W");	eprint("sunRiseSet:longitude: " + longitude);
	
	if (isNaN(latitude) || isNaN(longitude))
	{
		eprint("sunRiseSet:invalidLocation");
		return bf("invalidLocation");
	}

	var len   = isoDate.length;
	var year  = parseInt(isoDate.substring(0,       len - 4), 10);
	var month = parseInt(isoDate.substring(len - 4, len - 2), 10);
	var day   = parseInt(isoDate.substring(len - 2         ), 10);

	var theDate = new Date(year, month - 1, day, 12, 0, 0);	// offset at 12:00:00

	var localOffset = timeZoneOffset;	// in hours
		
	if (timeZoneOffset == 24 )			// use local time
	{
		tzOffset = theDate.getTimezoneOffset();	// in minutes
		localOffset = 0 - (tzOffset / 60);
	}
			
	var theta = 0.0321*Math.sqrt(altitudePref);	// converts altitude in metres to zenith delta
	
	var zenith  = zenithPref;	// type of sunrise/sunset
	
	var zenithType = zenithTypeOf(zenith);
	
	zenith += theta;			// add altitude adjustment

	var h12 = (preferences.hour12Pref.value == "1");
	
	var rises = calculate(true,  year, month, day, latitude, longitude, zenith, localOffset, h12);
	if (rises.indexOf('No') < 0) { rises = zenithType + ' sunrise at ' + rises; }
	
	var sets  = calculate(false, year, month, day, latitude, longitude, zenith, localOffset, h12);
	if (sets.indexOf('No') < 0)  { sets =  zenithType + ' sunset  at ' + sets; }
	
	var moonResult = '';
	var moonData = moonRiseSet(latitude, longitude, theDate, 0 - localOffset, h12).split('/');
	if (moonData.length < 2)
	{
		if (moonData[0] == 'Down all day') { moonResult = 'Moon is down all day'; } else { moonResult = 'Moon is up all day'; }
	}
	else
	{
		moonData[0] = moonData[0].replace(/S /, 'Moon sets  at ').replace(/R /, 'Moon rises at ').replace(/None/, '** ** **');
		moonData[1] = moonData[1].replace(/S /, 'Moon sets  at ').replace(/R /, 'Moon rises at ').replace(/None/, '** ** **');
		
		moonResult = moonData[0] + '\n' + moonData[1];
	}
	
	var result =  rises + '\n' + sets + '\n\n' + moonResult;
	eprint('sunRiseSet(' + isoDate + '):\n' + result);
	return result;
}

function updateSunPrefs()
{
	var latitude, longitude;
	timeZonePref  = preferences.timeZonePref.value;
	zenithPref    = Number(preferences.zenithPref.value);

	if (timeZonePref == 'Universal Time')
	{
		timeZoneOffset = 0;
	}
	else if (timeZonePref == 'My Local Time')
	{
		timeZoneOffset = 24;	// marker for local time
	}
	else
	{
		timeZoneOffset = parseFloat(preferences.timeZoneOffset.value);
		
		if (isNaN(timeZoneOffset)) { timeZoneOffset = 48; }
		else
		{
			timeZoneOffset = Math.round(4*timeZoneOffset) / 4;
			
			if ((timeZoneOffset > 14) || (timeZoneOffset < -12)) { timeZoneOffset = 48; }
		}
		if (timeZoneOffset == 48) { beep(); alert(bf('invalidZone')); timeZoneOffset = 0; }
		else { preferences.timeZoneOffset.value = timeZoneOffset.toString(); }
	}

	var K = preferences.latitudePref.value;		eprint("updateSunPrefs:K:  " + K);
	var L = preferences.longitudePref.value;	eprint("updateSunPrefs:L:  " + L);

	if ((K !== "") && (L !== ""))
	{
		latitude  = get_ang(K,  90, "N", "S");	eprint("updateSunPrefs:latitude:  " + latitude);
		longitude = get_ang(L, 180, "E", "W");	eprint("updateSunPrefs:longitude: " + longitude);

		if (isNaN(latitude) || isNaN(longitude))
		{
			eprint("updateSunPrefs:invalidLocation");
			//return bf("invalidLocation");
		}
	}

 	altitudePref = parseFloat(preferences.altitudePref.value);
 	altitudeUnitsPref = preferences.altitudeUnitsPref.value;
 			
 	if (isNaN(altitudePref)) { altitudePref = 0; }
 	if (altitudePref < 0) { altitudePref = 0; }
 			
 	if ((altitudeUnitsPref == "metres") && (altitudePref > altitudeMetresMax))
 	{
 		altitudePref = altitudeMetresMax;
 	}
 	else if ((altitudeUnitsPref == "feet") && (altitudePref > altitudeFeetMax))
 	{
 		altitudePref = altitudeFeetMax;
 	}
 			
 	preferences.altitudePref.value = String(altitudePref);			// rewrite modified value
 			
 	if (altitudeUnitsPref == "feet") { altitudePref *= 0.3048; }	// convert feet to metres
}

function saveImage(path, focus)
{
	eprint('saveImage:path: ' + path + ', focus: ' + focus);
	
	var opacity = 0; if (focus) { opacity = 255; }
	
	if (path) { showButtons(opacity); main_frame.saveImageToFile(path, "png"); return; }

	var imageDir = preferences.imageFolderPref.value;
	if (imageDir === "") { alert("Please choose an Image Folder in the Error preferences pane, then try again."); return; }
	
	var theDate  = new Date();
	var theYear  = theDate.getFullYear();
	var theMonth = theDate.getMonth() + 1;
	
	var year  = Number(preferences.yearPref.value);
	var month = Number(preferences.monthPref.value);
	var fileName;
	
	if ((year != theYear) || (month != theMonth))
	{
		fileName = makeISODate3(year, month, 1).substring(0, 6);
	}
	else
	{
		fileName = makeISODate1(theDate);
	}
	showButtons(opacity);
	main_frame.saveImageToFile(imageDir + "/" + fileName + ".png", "png");
}

function setToday()
{
	 updateDate(true);
}


function setDate(year, month)
{
	preferences.yearPref.value  = String(year);
	preferences.monthPref.value = String(month);
	updateCalendar();
}

function parseTellCommand(data)
{									// data   ::= action ":" params
									// params ::= (param) (";" param)*
									// param  ::= name "=" value
	var params = [], k = 0;
	var action;

	var lookFor = /^(\w+)\:(?:(\w+)\=([^\;]+))((?:\;(?:\w+)\=(?:[^\;]+))*)$/;
	var found = data.match(lookFor);
	if (found !== null)
	{
		action = found[1];
		params[k] = [ found[2], found[3] ];
		k += 1;

		while (found[4])
		{
			data = found[4];
			lookFor = /^(\;)(\w+)\=([^\;]+)((?:\;(?:\w+)\=(?:[^\;]+))*)$/;
			found = data.match(lookFor);
			if (found !== null)
			{
				params[k] = [ found[2], found[3] ];
				k += 1;
			}
		}
		return [ action, params ];	// returns array with [0] = action and [1] =  params
	}
	else { return null; }
}

var handleExternalCall = function()
{
	lprint('sed: ' + system.event.data);
	var command, action, params, item, name, value, name1, value1;
	command = parseTellCommand(system.event.data);	// "saveImage:path=/Users/nhw/Desktop/The_Calendar.png";focus=true"
	if (command !== null)
	{
		action = command[0];	// "saveImage"
		params = command[1];	// [ [ "path", "/Users/nhw/Desktop/The_Calendar.png" ], [ "focus", "true" ] ]

    	if (action == "saveImage")
    	{
			item   = params[0];
			name   = item[0];
			value  = item[1];
			
			item1  = params[1];
			name1  = item1[0];
			value1 = item1[1];

        	if ((name  == "path") && (name1  == "focus")) { saveImage(value, (value1 == "true")); }
    	}
    	else if (action == "setToday")			// "setToday:date=today"
    	{
			item  = params[0];
			name  = item[0];
			value = item[1];
        
        	if ((name  == "date") && ( value == "today")) { setToday(); }
    	}
    	else if (action == "setDate")			// "setDate:year=2008;month=12"
    	{
			item  = params[0];
			name  = item[0];
			value = item[1];
        
			item1  = params[1];
			name1  = item1[0];
			value1 = item1[1];
			
        	if ((name  == "year") && (name1  == "month")) { setDate( parseInt(value, 10), parseInt(value1, 10) ); }
    	}
    	else if (action == "close")			// "close:confirm=yes"
    	{
			item  = params[0];
			name  = item[0];
			value = item[1];
        
        	if ((name  == "confirm") && ( value == "yes")) { closeWidget(); }
    	}
	}
};

function openHelp()
{
	filesystem.open('Resources/Help.pdf');
}

function updateMemoFolder()
{
	if (multiUser) { return preferences.sharedDirectoryPref.value; }

	if (preferences.memoFolderPref.value == "1") { return system.widgetDataFolder; }

	var res, result;
	var oldPath = system.widgetDataFolder;
	var newPath = system.userDocumentsFolder + '/Month Calendar';
	
	if (filesystem.itemExists(newPath) && filesystem.isDirectory(newPath))	// The newPath directory already exists, so use it as the memo folder.
	{
		return newPath;
	}
	else if (filesystem.itemExists(newPath))	// The newPath is already in use for a file.
	{
		beep();
		alert(bf('cannotUpdateMemoFolder') + newPath + bf('alreadyExists'));
		eprint('updateMemoFolder:Cannnot update Memo Folder because ' + newPath + ' already exists.\nPlease read the manual for further instructions.');
		return oldPath;
	}
	else	// Copy files from the old folder to the new folder.
	{
		beep();
		res = alert(bf('relocating'),bf('Allow'),bf('Deny'));
		if (res == 2) { return oldPath; }
		
		eprint('updateMemoFolder:oldPath=' + oldPath);
		eprint('updateMemoFolder:newPath=' + newPath);

		result = filesystem.copy(oldPath, newPath);
		eprint('updateMemoFolder:copy=' + result);
		
		if (result)
		{
			eprint('updateMemoFolder:remove=' + filesystem.removeDirectoryContents(oldPath));
			return newPath;
		}
		else
		{
			beep();
			alert(bf('uodatingFailed'));
			eprint('updateMemoFolder:Updating of Memo Folder failed.\nPlease read the manual for further instructions.');
			//closeWidget();
			return oldPath;
		}
	}
}
