#include "hw4.h"

Money::Money(double amnt,Currency ty, double rt) 
{
	if(amnt<0) throw NEGATIVEAMOUNT;
	if(rt<0) throw NEGATIVERATE;
	amount=amnt;
	type=ty;
	rate=rt;
}

Transaction::Transaction(TransType newtype,Money newmoney):trans_money(newmoney.amount,newmoney.type,newmoney.rate)
{
	time_t t;
	struct tm *l;
	t_type=newtype;
	
	time(&t);
	l=localtime(&t);
	trans_time=*l;
	calendertime=t;
}

Transaction::Transaction(TransType newtype,Money newmoney,int userid,const char username[]):trans_money(newmoney.amount,newmoney.type,newmoney.rate)
{
	time_t t;
	struct tm *l;
	t_type=newtype;
	trans_id=userid;
	strcpy(trans_ownername,username);
	time(&t);
	l=localtime(&t);
	trans_time=*l;
	calendertime=t;	
}

double Transaction::operator!()
{	
	if(t_type==WITHDRAW || t_type==TRANSOUT)		
		return -(trans_money.amount*trans_money.rate);
	else
		return trans_money.amount*trans_money.rate;
}

int Transaction::operator<(Transaction &rhs)
{
	if(calendertime<rhs.calendertime) return 1;
	else return 0;
}

ostream & operator<< (ostream & out,Transaction &myTrans)
{
	char p[15]; 
	out.setf(ios::fixed,ios::floatfield);
          if (myTrans.t_type==OPEN) out<<"Open ";
          else if(myTrans.t_type==WITHDRAW) out<<"Wit. ";
          else if(myTrans.t_type==DEPOSIT) out<<"Dep. "; 
          else if(myTrans.t_type==TRANSIN) out<<"TrFr "; 
          else if(myTrans.t_type==TRANSOUT) out<<"TrTo "; 
          
          strftime(p,15,"%d-%m-%y %H:%M",&(myTrans.trans_time));
			out<<p<<" ";

	if (myTrans.t_type!=TRANSIN && myTrans.t_type!=TRANSOUT) 
	{
		out.setf(ios::right);
		out.width(15);
		out<<"";
	}
	else { out.setf(ios::right);     
	    if(strlen(myTrans.trans_ownername)<=10)   
	    { 
	    	out.width(10);
		out<<myTrans.trans_ownername;
	    }
             else{							   
			out.setf(ios::left); 
			int t=0;
			while(t!=10){
			       out<<myTrans.trans_ownername[t];
			       t++;
			     }
			     }
	out.setf(ios::right); 
	out.width(5);
	out<<myTrans.trans_id;
	}
	cout<<" ";
	
	if(myTrans.trans_money.type==TL) 
	{
	       out.setf(ios::right);   
	       out.width(10);
	       out.precision(0);
	       out<<myTrans.trans_money.amount;
	       cout<<"TL";
	}
             else if (myTrans.trans_money.type==DOLAR) 
             {    
		out.setf(ios::right);        
	         out.width(10);
	         out.precision(2);
		 out <<myTrans.trans_money.amount;  
	         cout<<"$ ";
          }
	else if (myTrans.trans_money.type==EURO) 
	{  
		 out.setf(ios::right);   
		 out.width(10); 
		 out.precision(2);
		 out<<myTrans.trans_money.amount;        
		 cout<<"Eu";
	} 
          cout<<" ";
         out.setf(ios::right);
         out.width(8);   
         out.precision(0);
         out<<myTrans.trans_money.rate;
                      
         cout<<" ";
         out.setf(ios::right);
	out.width(11);
	out.precision(0); 
	 out<<myTrans.operator!(); 
	cout<<" ";
	return out;
}

Account::Account(int newid,const char newname[],Money newmoney)
{
	id=newid;
	strcpy(ownername,newname);
	currentmoney=newmoney.amount*newmoney.rate;
	head=tail=new Transst(OPEN,newmoney);
}

Account::Account(int newid,const char newname[])
{
	Money newmoney;
	id=newid;
	strcpy(ownername,newname);
	currentmoney=0.0;
	head=tail=new Transst(OPEN,newmoney);
}

int Account::getid()
{
	return id;
}

double Account::getamount()
{
	return currentmoney;
}

const char * Account::getname()
{
	return ownername;
}

void Account::CopyTransactions(Account &rhs)
{
	Transst *tmp;
	tmp=rhs.head;

	do
	{
		if(!head)
		{
			head=tail=new Transst(OPEN,Money());
			head->t=tmp->t;			
		}
		else
		{ 
			tail=tail->next=new Transst(OPEN,Money());
			tail->t=tmp->t;
		}
		
		tmp=tmp->next;
		
	}while(tmp);
}

Account::Account(Account &rhs)
{
	id=rhs.id;
	strcpy(ownername,rhs.ownername);
	currentmoney=rhs.currentmoney;
	CopyTransactions(rhs);	
}

void Account::DellTransactions()
{
	Transst *tmp;
	tmp=head;
	while(tmp)
	{
		tmp=tmp->next;
		delete head;
		head=tmp;			
	}
}


Account & Account::operator=(Account &rhs)
{
	currentmoney=rhs.getamount();
	DellTransactions();
	CopyTransactions(rhs);
	return *this;	
}

Account & Account::operator+(Money newmoney)
{
	currentmoney+=newmoney.amount*newmoney.rate;
	tail=tail->next=new Transst(DEPOSIT,newmoney);
	return *this;
}

Account & Account::operator-(Money newmoney)
{
	if(currentmoney<newmoney.amount*newmoney.rate) throw NOTENOUGHAMOUNT;
	currentmoney-=newmoney.amount*newmoney.rate;
	tail=tail->next=new Transst(WITHDRAW,newmoney);
	return *this;
}

Account & Account::transferto(Account &toaccount,Money transfermoney)
{
	if((transfermoney.amount)*(transfermoney.rate)>currentmoney) throw NOTENOUGHAMOUNT;
	currentmoney-=transfermoney.amount*transfermoney.rate;
	tail=tail->next=new Transst(TRANSOUT,transfermoney,toaccount.id,toaccount.ownername);
	toaccount.currentmoney+=(transfermoney.amount)*(transfermoney.rate);
	toaccount.tail=toaccount.tail->next=new Transst(TRANSIN,transfermoney,id,ownername);
	
	return *this;
}

Account &Account::transferfrom(Account &fromaccount,Money transfermoney)
{
	if((transfermoney.amount)*(transfermoney.rate)>fromaccount.currentmoney) throw NOTENOUGHAMOUNT;
	currentmoney += (transfermoney.amount)*(transfermoney.rate);
	tail=tail->next=new Transst(TRANSIN,transfermoney,fromaccount.id,fromaccount.ownername);
	fromaccount.currentmoney-=transfermoney.amount*transfermoney.rate;
	fromaccount.tail=fromaccount.tail->next=new Transst(TRANSOUT,transfermoney,id,ownername);
	return *this;
}

Account & Account::operator|=(Account &rhs)
{
	Transst *tmp,*tmpl,*tmpr,*index;
	if(!(head->t)+ !((rhs.head)->t)<0) throw NOTENOUGHAMOUNT;
	
	Money m(!(head->t)+ !((rhs.head)->t),TL,1.0);
	Transaction transtemp(OPEN,m);
	currentmoney=!(head->t)+ !((rhs.head)->t);
	head->t=transtemp;
		
	index=head;
	tmpl=head->next;
	tmpr=(rhs.head)->next;
	
	while(tmpl || tmpr)
	{
		if(tmpr && (!tmpl || ((tmpr->t)<(tmpl->t))))
		{
			tmp=new Transst(OPEN,Money());
			tmp->t=tmpr->t;
			currentmoney+=!(tmpr->t);
			if(currentmoney<0) throw NOTENOUGHAMOUNT;
			tmp->next=index->next;
			index->next=tmp;
			index=tmp;
			tmpl=index->next;
			tmpr=tmpr->next;									
		}
		else
		{
			currentmoney+=!(tmpl->t);
			if(currentmoney<0) throw NOTENOUGHAMOUNT;
			index=index->next;
			tmpl=index->next;						
		}		
	}
	return *this;
}

ostream & operator<<(ostream & out,Account & thisaccount)
{
	double tike;
	Account::Transst *tmp;
	out.setf(ios::fixed,ios::floatfield);
	out.precision(0);   
          out<<"Account id: "<<thisaccount.id<<endl;
	out<<"Account owner: "<<thisaccount.ownername<<endl;
	out<<"Current amount: "<<thisaccount.currentmoney<<"TL"<<endl;
	out<<"Op  |     Time     |  Explanation  |    Amount  | Rate   |Effective  |Tot. after"<<endl;
	out<<"----+--------------+---------------+------------+--------+-----------+----------"<<endl;
          tmp=thisaccount.head->next;
	out<<thisaccount.head->t;
          tike=(thisaccount.head->t).operator!(); 
	 out.setf(ios::right);
	 out.width(10);
	 out<<tike<<endl;
	 while(tmp)
	 {
	 	out<<(tmp->t);
		tike+=(tmp->t).operator!();
		out.width(10); 
		out<<tike<<endl;    
		tmp=tmp->next;
	  }

	return out;
}

void CopyAccounts(Bank &lhs,Bank &rhs)
{
	Bank::Accountst *tmp;
	tmp=rhs.head;

	while(tmp)
	{
		if(!lhs.head)
			lhs.head=lhs.tail=new Bank::Accountst(*tmp);
		else
			lhs.tail=(lhs.tail)->next=new Bank::Accountst(*tmp);
		
		tmp=tmp->next;	
	}
}

const char *Getbankname(Bank &rhs)
{
	return rhs.bankname;
}

Bank::Bank(Bank &rhs)
{
	strcpy(bankname,Getbankname(rhs));
	CopyAccounts(*this,rhs);	
	
}

void Bank::newAccount(int newid,const char newname[])
{
	Accountst *tmp2;
	Accountst *tmp;
	tmp=new Accountst(newid,newname);
	tmp2=head;
	
	if(head==NULL)
		head=tail=tmp;
	else
		if(newid<(head->t).getid())
		{
			tmp->next=head;
			head=tmp;
		}
		else 
			if(newid==(head->t).getid())
			throw ACCOUNTEXISTS;	
			else
			{
			while(tmp2->next && newid>(tmp2->next->t).getid())
			tmp2=tmp2->next;
	
			if(tmp2->next && newid==(tmp2->next->t).getid())
			throw ACCOUNTEXISTS;
			else
				if(!(tmp2->next))
				tmp2->next=tmp;
				else
				{
					tmp->next=tmp2->next;
					tmp2->next=tmp;
				}
			}
}

void Bank::newAccount(int newid,const char newname[],Money newmoney)
{
	Accountst *tmp,*tmp2;
	tmp=new Accountst(newid,newname,newmoney);
	tmp2=head;
	if(head==NULL)
		head=tail=tmp;
	else
		if(newid<(head->t).getid())
		{
			tmp->next=head;
			head=tmp;
		}
		else 
			if(newid==(head->t).getid())
			throw ACCOUNTEXISTS;	
			else
			{
				while(tmp2->next && newid>(tmp2->next->t).getid())
				tmp2=tmp2->next;
	
				if(tmp2->next && newid==(tmp2->next->t).getid())
				throw ACCOUNTEXISTS;
				else
					if(!(tmp2->next))
					tmp2->next=tmp;
					else
					{
						tmp->next=tmp2->next;
						tmp2->next=tmp;
					}
			}
}

void Bank::DellAccounts()
{
	Accountst *tmp;
	while(head)
	{
		tmp=head->next;
		delete head;
		head=tmp;	
	}
}

Bank & Bank::operator=(Bank &rhs)
{
	DellAccounts();
	CopyAccounts(*this,rhs);
	return *this;
}

Account & Bank::operator[](int searchid)
{
	Accountst *tmp;
	tmp=head;
	while(tmp)
	{
		if((tmp->t).getid()==searchid)
		return tmp->t;
		else
		tmp=tmp->next;
	}
	throw ACCOUNTNOTFOUND;	
}

Account & Bank::operator[](const char searchname[])
{
	Accountst *tmp;
	tmp=head;
	while(tmp)
	{
		if(!strcasecmp(searchname,(tmp->t).getname()))
		return tmp->t;
		else
		tmp=tmp->next;
	}
	throw ACCOUNTNOTFOUND;
}

void Bank::deleteAccount(int searchid)
{
	Accountst *tmp,*tmp2;
	if(!head) throw ACCOUNTNOTFOUND;
	if((head->t).getid()==searchid)
	{
		tmp=head;
		head=head->next;
		delete head;
	}
	else
	{
		tmp=head;
		while(tmp->next && (tmp->next->t).getid()!=searchid)
		tmp=tmp->next;
		if(!(tmp->next))
		throw ACCOUNTNOTFOUND;
		else
		{
			tmp2=tmp->next;
			tmp->next=tmp->next->next;
			delete(tmp2);
		}
	}
}

void Bank::deleteAccount(const char searchname[])
{
	Accountst *tmp,*tmp2;
	if(!head) throw ACCOUNTNOTFOUND;
	if(!strcasecmp((head->t).getname(),searchname))
	{
		tmp=head;
		head=head->next;
		delete head;
	}
	else
	{
		tmp=head;
		while(tmp->next && strcasecmp((tmp->next->t).getname(),searchname))
		tmp=tmp->next;
		if(!(tmp->next))
		throw ACCOUNTNOTFOUND;
		else
		{
			tmp2=tmp->next;
			tmp->next=tmp->next->next;
			delete(tmp2);
		}
	}
}

ostream &operator<<(ostream &out,Bank &thisbank)
{
	Bank::Accountst *tmp;
	
	out<<"Bank name: "<<thisbank.bankname<<endl;
	out<<"Account id|    Account owner                         |Current amount(TL)"<<endl;           
	out<<"----------+------------------------------------------+----------------"<<endl;		    
	 
	tmp=thisbank.head;
	while(tmp)
	{
         	out.setf(ios::right);   
	        out.width(10);    
	        out<<tmp->t.getid();
		cout<<" ";
		           
		out.setf(ios::left);
		out.width(42);
		out<<tmp->t.getname();
		cout<<" ";     						   
						   
		out.unsetf(ios::left);
		out.setf(ios::right); 
		out.width(16);    
		out<<(tmp->t).getamount()<<endl;
		tmp=tmp->next;
	}

	return out;	
}

Account::~Account()
{
	Transst *tmp;
	tmp=head;
	while(tmp)
	{
		tmp=head->next;
		delete head;
		head=tmp;
	}
}

Bank::~Bank()
{
	Accountst *tmp;
	tmp=head;
	while(tmp)
	{
		tmp=head->next;
		delete head;
		head=tmp;
	}
}
