using System;
using System.Net;
using System.Net.Sockets;
using banco1.presentacion.comunic;
using banco1.domain;

namespace banco1.comunicaciones
{
	public class Servidor
	{
		public Socket mServidor, mCliente;
		public IPEndPoint mIP;
		public int mPuerto;
		protected FServidor mVentana;
		public AsyncCallback pfnWorkerCallBack;
		public bool mClienteConectado, mFinDeOrden;
		private String mOrden="";
		
		public Servidor(FServidor f, int puerto)
		{
			mVentana=f;
			mPuerto=puerto;
			mIP=new IPEndPoint(IPAddress.Any, mPuerto);
			mServidor = new Socket(AddressFamily.InterNetwork,
				SocketType.Stream, ProtocolType.Tcp);
			mServidor.Bind(mIP);
			mServidor.Listen(16);
			mServidor.BeginAccept(new AsyncCallback(OnClientConnect),null);
			mClienteConectado=false;
		}
		
		public void Enviar (string msg)
		{
			try
			{
				Object objData = msg;
				byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString ());
				this.mCliente.Send (byData);
			}
			catch(Exception se)
			{
				mVentana.logError(se.Message);
			}
		}

		public override int GetHashCode ()
		{
			if (mServidor != null)
			{
				IPEndPoint aux = (IPEndPoint) mServidor.LocalEndPoint;
				return aux.Port;
			}
			else
			{
				return 0;
			}
		}

		public class CSocketPacket
		{
			public System.Net.Sockets.Socket thisSocket;
			public byte[] dataBuffer = new byte[1];
		}

		private void OnClientConnect(IAsyncResult asyn)
		{
			try
			{
				mClienteConectado = true;
				mCliente = mServidor.EndAccept (asyn);
				WaitForData(mCliente);
			}
			catch(Exception se)
			{
				mVentana.logError(se.Message);
			}
			
		}

		private void WaitForData(System.Net.Sockets.Socket soc)
		{
			try
			{
				if  ( pfnWorkerCallBack == null ) 
				{
					pfnWorkerCallBack = new AsyncCallback (OnDataReceived);
				}
				CSocketPacket theSocPkt = new CSocketPacket ();
				theSocPkt.thisSocket = soc;
				// now start to listen for any data...
				soc.BeginReceive (theSocPkt.dataBuffer ,0,theSocPkt.dataBuffer.Length ,SocketFlags.None,pfnWorkerCallBack,theSocPkt);
			}
			catch(Exception se)
			{
				mVentana.logError(se.Message);
			}

		}

		private  void OnDataReceived(IAsyncResult asyn)
		{
			try
			{
				CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState ;
				//end receive...
				int iRx  = 0 ;
				iRx = theSockId.thisSocket.EndReceive (asyn);
				char[] chars = new char[iRx +  1];
				System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
				int charLen = d.GetChars(theSockId.dataBuffer, 0, iRx, chars, 0);
				System.String orden = new System.String(chars);
				if (orden[0]=='#') 
				{
					this.mFinDeOrden=true;
					procesaOrden();
				} 
				else 
					mOrden+=orden[0];
				if (mClienteConectado)
					WaitForData(mCliente);
			}
			catch(Exception se)
			{
				mVentana.logError(se.Message);
			}
		}

		protected void procesaOrden() 
		{
			if (mOrden.StartsWith("SACAR")) 
			{
				String numeroTarjeta=mOrden.Substring(5, 16);
				String sImporte=mOrden.Substring(21);
				double importe=double.Parse(sImporte);
				try 
				{
					Tarjeta t=Debito.getTarjeta(numeroTarjeta);
					t.sacarDinero(importe);
					this.Enviar("ACEPTADA#");
				}
				catch (Exception ex) 
				{
					this.Enviar("ERROR" + ex.Message);
				}
			}
		}

		public void close ()
		{
			try 
			{
				mServidor.Close ();
				mCliente.Close ();
				mClienteConectado = false;
			}
			catch (Exception ex)
			{
				mVentana.logError(ex.Message);
			}
	
		}
	}
}
