//this file is part of eMule
//Copyright (C)2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
//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 "stdafx.h"
#include "emule.h"
#include "Scheduler.h"
#include "OtherFunctions.h"
#include "ini2.h"
#include "Preferences.h"
#include "DownloadQueue.h"
#include "emuledlg.h"
#include "MenuCmds.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


CScheduler::CScheduler(){
	LoadFromFile();
	SaveOriginals();
	m_iLastCheckedMinute=60;
}

CScheduler::~CScheduler(){
	SaveToFile();
	RemoveAll();
}

int CScheduler::LoadFromFile(){

	CString strName;
	CString temp;

	strName.Format(_T("%spreferences.ini"),thePrefs.GetConfigDir());
	CIni ini(strName, _T("Scheduler"));
	
	UINT max=ini.GetInt(_T("Count"),0);
	UINT count=0;

	while (count<max) {
		strName.Format(_T("Schedule#%i"),count);
		temp=ini.GetString(_T("Title"),_T(""),strName);
		if (temp!=_T("")) {
			Schedule_Struct* news= new Schedule_Struct();
			news->title=temp;
			news->day=ini.GetInt(_T("Day"),0);
			news->enabled=ini.GetBool(_T("Enabled"));
			news->time=ini.GetInt(_T("StartTime"));
			news->time2=ini.GetInt(_T("EndTime"));
			ini.SerGet(true, news->actions,
				ARRSIZE(news->actions), _T("Actions"));
			ini.SerGet(true, news->values,
				ARRSIZE(news->values), _T("Values"));

			AddSchedule(news);
			count++;
		} else break;
	}

	return count;
}

void CScheduler::SaveToFile(){

	CString temp;
	Schedule_Struct* schedule;

	CIni ini(thePrefs.GetConfigFile(), _T("Scheduler"));
	ini.WriteInt(_T("Count"), GetCount());
	
	for (uint8 i=0; i<GetCount();i++) {
		schedule=theApp.scheduler->GetSchedule(i);

		temp.Format(_T("Schedule#%i"),i);
		ini.WriteString(_T("Title"),schedule->title,temp);
		ini.WriteInt(_T("Day"),schedule->day);
		ini.WriteInt(_T("StartTime"),schedule->time);
		ini.WriteInt(_T("EndTime"),schedule->time2);
		ini.WriteBool(_T("Enabled"),schedule->enabled);

		ini.SerGet(false, schedule->actions,
			ARRSIZE(schedule->actions), _T("Actions"));
		ini.SerGet(false, schedule->values,
			ARRSIZE(schedule->values), _T("Values"));
	}
}

void CScheduler::RemoveSchedule(int index){
	
	if (index>=schedulelist.GetCount()) return;

	Schedule_Struct* todel;
	todel=schedulelist.GetAt(index);
	delete todel;
	schedulelist.RemoveAt(index);
}

void CScheduler::RemoveAll(){
	while( schedulelist.GetCount()>0 )
		RemoveSchedule(0);
}

int CScheduler::AddSchedule(Schedule_Struct* schedule) {
	schedulelist.Add(schedule);
	return schedulelist.GetCount()-1;
}

int CScheduler::Check(bool forcecheck){
	if (!thePrefs.IsSchedulerEnabled()
		|| theApp.scheduler->GetCount()==0
		|| !theApp.emuledlg->IsRunning()) return -1;

	Schedule_Struct* schedule;
	CTime tNow=CTime(safe_mktime(CTime::GetCurrentTime().GetLocalTm()));
	
	if (!forcecheck && tNow.GetMinute()==m_iLastCheckedMinute) return -1;

	m_iLastCheckedMinute=tNow.GetMinute();
	theApp.scheduler->RestoreOriginals();

	for (uint8 si=0;si<theApp.scheduler->GetCount();si++) {
		schedule=theApp.scheduler->GetSchedule(si);
		if (schedule->actions[0]==0 || !schedule->enabled) continue;

		// check day of week
		if (schedule->day!=DAY_DAYLY) {
			int dow=tNow.GetDayOfWeek();
			switch (schedule->day) {
				case DAY_MO : if (dow!=2) continue;
					break;
				case DAY_DI : if (dow!=3) continue;
					break;
				case DAY_MI : if (dow!=4) continue;
					break;
				case DAY_DO : if (dow!=5) continue;
					break;
				case DAY_FR : if (dow!=6) continue;
					break;
				case DAY_SA : if (dow!=7) continue;
					break;
				case DAY_SO : if (dow!=1) continue;
					break;
				case DAY_MO_FR : if (dow==7 || dow==1 ) continue;
					break;
				case DAY_MO_SA : if (dow==1) continue;
					break;
				case DAY_SA_SO : if (dow>=2 && dow<=6) continue;
			}
		}

		//check time
		UINT h1,h2,m1,m2;
		CTime t1=CTime(schedule->time);
		CTime t2=CTime(schedule->time2);
		h1=t1.GetHour();	h2=t2.GetHour();
		m1=t1.GetMinute();	m2=t2.GetMinute();
		int it1,it2, itn;
		it1=h1*60 + m1;
		it2=h2*60 + m2;
		itn=tNow.GetHour()*60 + tNow.GetMinute();
		if (it1<=it2) { // normal timespan
			if ( !(itn>=it1 && itn<it2) ) continue;
		} else {		   // reversed timespan (23:30 to 5:10)  now 10
			if ( !(itn>=it1 || itn<it2)) continue;
		}

		// ok, lets do the actions of this schedule
		ActivateSchedule(si,schedule->time2==0);
	}

	return -1;
}

void CScheduler::SaveOriginals() {
	original_upload=thePrefs.GetMaxUpload();
	original_download=thePrefs.GetMaxDownload();
	original_connections=thePrefs.GetMaxConnections(true); // NEO: QS - [QuickStart] <-- Xanatos --
	original_cons5s=thePrefs.GetMaxConperFive(true); // NEO: QS - [QuickStart] <-- Xanatos --
	original_sources=thePrefs.GetMaxSourcePerFileDefault();
#ifdef NEO_BC // NEO: NBCs - [NeoBandwidthControlSheduler] -- Xanatos -->
	original_conshalf=thePrefs.GetMaxHalfConnections(true); // NEO: QS - [QuickStart]

	original_UlMode=thePrefs.GetUploadControl();
	original_UlMax=thePrefs.GetMaxBCUpload();
	original_UlMin=thePrefs.GetMinBCUpload();

	original_UpBasePing=thePrefs.GetBasePingUp();
	original_UpTolerance=thePrefs.GetPingUpTolerance();
	original_UpProzent=thePrefs.GetPingUpProzent();

	original_UpStream=thePrefs.GetMaxUpStream();

	original_DlMode=thePrefs.GetDownloadControl();
	original_DlMax=thePrefs.GetMaxBCDownload();
	original_DlMin=thePrefs.GetMinBCDownload();

	original_DownBasePing=thePrefs.GetBasePingDown();
	original_DownTolerance=thePrefs.GetPingDownTolerance();
	original_DownProzent=thePrefs.GetPingDownProzent();

	original_DownStream=thePrefs.GetMaxDownStream();
	
	original_ManageCons=thePrefs.IsManageConnections();
	original_ManageFactor=thePrefs.GetManageConnectionsFactor();

	original_IncludeOh=thePrefs.IsIncludeOverhead();
#endif // NEO_BC // NEO: NBCs END <-- Xanatos --
}

void CScheduler::RestoreOriginals() {
	thePrefs.SetMaxUpload(original_upload);
	thePrefs.SetMaxDownload(original_download);
	thePrefs.SetMaxConnections(original_connections);
	thePrefs.SetMaxConsPerFive(original_cons5s);
	thePrefs.SetMaxSourcesPerFile(original_sources);
#ifdef NEO_BC // NEO: NBCs - [NeoBandwidthControlSheduler] -- Xanatos -->
	thePrefs.SetMaxHalfConnections(original_conshalf);

	thePrefs.SetUploadControl(original_UlMode);
	thePrefs.SetMaxBCUpload(original_UlMax);
	thePrefs.SetMinBCUpload(original_UlMin);

	thePrefs.SetBasePingUp(original_UpBasePing);
	thePrefs.SetPingUpTolerance(original_UpTolerance);
	thePrefs.SetPingUpProzent(original_UpProzent);

	thePrefs.SetMaxUpStream(original_UpStream);

	thePrefs.SetDownloadControl(original_DlMode);
	thePrefs.SetMaxBCDownload(original_DlMax);
	thePrefs.SetMinBCDownload(original_DlMin);

	thePrefs.SetBasePingDown(original_DownBasePing);
	thePrefs.SetPingDownTolerance(original_DownTolerance);
	thePrefs.SetPingDownProzent(original_DownProzent);

	thePrefs.SetMaxDownStream(original_DownStream);
	
	thePrefs.SetManageConnections(original_ManageCons);
	thePrefs.SetManageConnectionsFactor(original_ManageFactor);

	thePrefs.SetIncludeOverhead(original_IncludeOh);
#endif // NEO_BC // NEO: NBCs END <-- Xanatos --
}

void CScheduler::ActivateSchedule(int index,bool makedefault) {
	Schedule_Struct* schedule=GetSchedule(index);

#ifdef NEO_BC // NEO: NBCs - [NeoBandwidthControlSheduler] -- Xanatos -->
	for (int ai=0;ai<25;ai++) {
#else
	for (int ai=0;ai<16;ai++) {
#endif // NEO_BC // NEO: NBCs END <-- Xanatos --
		if (schedule->actions[ai]==0) break;
		if (schedule->values[ai]=="" /* maybe ignore in some future cases...*/ ) continue;

		switch (schedule->actions[ai]) {
			case 1 : thePrefs.SetMaxUpload(_tstoi(schedule->values[ai]));
				if (makedefault) original_upload=(uint16)_tstoi(schedule->values[ai]); 
				break;
			case 2 : thePrefs.SetMaxDownload(_tstoi(schedule->values[ai]));
				if (makedefault) original_download=(uint16)_tstoi(schedule->values[ai]);
				break;
			case 3 : thePrefs.SetMaxSourcesPerFile(_tstoi(schedule->values[ai]));
				if (makedefault) original_sources=_tstoi(schedule->values[ai]);
				break;
			case 4 : thePrefs.SetMaxConsPerFive(_tstoi(schedule->values[ai]));
				if (makedefault) original_cons5s=_tstoi(schedule->values[ai]);
				break;
			case 5 : thePrefs.SetMaxConnections(_tstoi(schedule->values[ai]));
				if (makedefault) original_connections=_tstoi(schedule->values[ai]);
				break;
			
			case 6 : theApp.downloadqueue->SetCatStatus(_tstoi(schedule->values[ai]),MP_STOP);
				break;
			case 7 : theApp.downloadqueue->SetCatStatus(_tstoi(schedule->values[ai]),MP_RESUME);
				break;

#ifdef NEO_BC // NEO: NBCs - [NeoBandwidthControlSheduler] -- Xanatos -->
			case 8 : thePrefs.SetMaxHalfConnections(_tstoi(schedule->values[ai]));
				if (makedefault) original_conshalf=_tstoi(schedule->values[ai]);
				break;


			case 9 : thePrefs.SetUploadControl(_tstoi(schedule->values[ai]));
				if (makedefault) original_UlMode=_tstoi(schedule->values[ai]);
				break;
			case 10 : thePrefs.SetMaxBCUpload((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_UlMax=(float)_wtof(schedule->values[ai]);
				break;
			case 11 : thePrefs.SetMinBCUpload((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_UlMin=(float)_wtof(schedule->values[ai]);
				break;

			case 12 : thePrefs.SetBasePingUp(_tstoi(schedule->values[ai]));
				if (makedefault) original_UpBasePing=_tstoi(schedule->values[ai]);
				break;
			case 13 : thePrefs.SetPingUpTolerance(_tstoi(schedule->values[ai]));
				if (makedefault) original_UpTolerance=_tstoi(schedule->values[ai]);
				break;
			case 14 : thePrefs.SetPingUpProzent(_tstoi(schedule->values[ai]));
				if (makedefault) original_UpProzent=_tstoi(schedule->values[ai]);
				break;

			case 15 : thePrefs.SetMaxUpStream((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_UpStream=(float)_wtof(schedule->values[ai]);
				break;


			case 16 : thePrefs.SetDownloadControl(_tstoi(schedule->values[ai]));
				if (makedefault) original_DlMode=_tstoi(schedule->values[ai]);
				break;
			case 17 : thePrefs.SetMaxBCDownload((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_DlMax=(float)_wtof(schedule->values[ai]);
				break;
			case 18 : thePrefs.SetMinBCDownload((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_DlMin=(float)_wtof(schedule->values[ai]);
				break;

			case 19 : thePrefs.SetBasePingDown(_tstoi(schedule->values[ai]));
				if (makedefault) original_DownBasePing=_tstoi(schedule->values[ai]);
				break;
			case 20 : thePrefs.SetPingDownTolerance(_tstoi(schedule->values[ai]));
				if (makedefault) original_DownTolerance=_tstoi(schedule->values[ai]);
				break;
			case 21 : thePrefs.SetPingDownProzent(_tstoi(schedule->values[ai]));
				if (makedefault) original_DownProzent=_tstoi(schedule->values[ai]);
				break;

			case 22 : thePrefs.SetMaxDownStream((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_DownStream=(float)_wtof(schedule->values[ai]);
				break;


			case 23 : thePrefs.SetManageConnections(I2B(_tstoi(schedule->values[ai])));
				if (makedefault) original_ManageCons=I2B(_tstoi(schedule->values[ai]));
				break;
			case 24 : thePrefs.SetManageConnectionsFactor((float)_tstoi(schedule->values[ai]));
				if (makedefault) original_ManageFactor=(float)_wtof(schedule->values[ai]);
				break;

			case 25 : thePrefs.SetIncludeOverhead(I2B(_tstoi(schedule->values[ai])));
				if (makedefault) original_IncludeOh=I2B(_tstoi(schedule->values[ai]));
				break;
#endif // NEO_BC // NEO: NBCs END <-- Xanatos --
		}
	}
}