package org.openremote.controller.protocol.ehouse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStreamReader;
import java.net.InetAddress;

public class ehousecommunication
{  public static boolean Initialized=false;
   public static boolean OpenRemoteTCPStatusEnabled=true;   //Open remote tcp servers for sensor status input
   public static boolean internet=false;
   public static String sep=System.getProperty("file.separator");
   public static String locpath=System.getProperty("user.dir")+sep+"ehouse"+sep;
   public static String eHousePath="";
   public static String path=locpath;
   static String VendorCodes;
   static String PassCodes;
   static String OpenRemotePort="4321";
    public static final int OPTIONS=3;
    final static int CHALLANGE_RESPONSE =0;
    final static int XOR_PASS           =1;
    final static int PLAIN_PASS         =2;
    final static int NOTHING            =3;   
    final static int FURTKA             =5;   
    protected static int[] PassWord    = new int[255];
    protected static int[] Vendor      = new int[255];
    static String enc="Cp1250"; 
    static String EOLN="\r\n";
    static String TCPLocalServer="192.168.0.254";
    static String TCPLocalPort="9876";
    static String UDPStatusReceptionPort="6789";
    protected static int[] RESP = new int[20];
    protected static int[] response = new int[255];
    protected static int[] Hashingin=new int[250];
    static boolean Terminate=false;
    protected static int EventSize=0;
    public static boolean WiFiSendOk=false;
    protected static byte[] EventToRunByte= new byte[256];
    protected static String EventToRun="";
    boolean eHouse1Support=false;
    boolean statusapp=false;
    private static boolean ValidDate =false;
    protected static String pass="";
    public static Socket ehouseconnection=null;            //Client Connection
    public static OutputStream ehouseout=null;            //output stream connection
    public static InputStream ehousein=null;              //input stream connection
    public static BufferedReader ehouseinput=null;
    public static int OpenRemotePortNr=4321;
        //public BufferedWrite ehouseoutput=null;
        int error=0;    
        String str="";
        static String log="";
        public static String[] Win = { "\u00b9", "\u00a5", "\u00e6", "\u00c6", "\u00ea", "\u00ca", "\u00b3", "\u00a3", "\u00f1", "\u00d1", "\u00f3", "\u00d3", "\u009c", "\u008c", "\u00bf", "\u00af", "\u009f", "\u008f" };        //windows code page
        public static String[] SmsFormat = { "\u0002", "\u0003", "\u0004", "\u0005", "\u0006", "\u0007", "\u0008", "\u000B", "\u000C", "\u000E", "\u000F", "\u0010", "\u0011", "\u0012", "\u0013", "\u0014", "\u0015", "\u0016" };  //sms coding polish chars to unused chars in order to not use 16b unicode 
        public static String[] UnicodeFormat = { "\u0105", "\u0104", "\u0107", "\u0106", "\u0119", "\u0118", "\u0142", "\u0141", "\u0144", "\u0143", "\u00F3", "\u00D3", "\u015B", "\u015A", "\u017C", "\u017B", "\u017A", "\u0179" };//unicode 16b
        
////////////////////////////////////////////////////////////////////////////////////////////////
public static String hix(int[] dta,int size)
{
    String res="";
    for (int i=0;i<size;i++)
        {
        res+=ehousecommunication.ConvertAsciHex(dta[i]);
        }
    return res;
}
//////////////////////////////////////////////////////////////////////////////////////////////        
public static String hib(Byte[] dta,int size)
{
    String res="";
    for (int i=0;i<size;i++)
        {
        res+=ehousecommunication.ConvertAsciHex(dta[i]);
        }
    return res;
}
/////////////////////////////////////////////////////////////////////////////////////        
protected static void MakeChalangeResponse(int mode)
{ 

if (mode==XOR_PASS) //xored password
    {
    response[6+0]=(response[0+0]^PassWord[0]^Vendor[0]) & 0xff;
    response[6+1]=(response[0+1]^PassWord[1]^Vendor[1]) & 0xff;
    response[6+2]=(response[0+2]^PassWord[2]^Vendor[2]) & 0xff;
    response[6+3]=(response[0+3]^PassWord[3]^Vendor[3]) & 0xff;
    response[6+4]=(response[0+4]^PassWord[4]^Vendor[4]) & 0xff;
    response[6+5]=(response[0+5]^PassWord[5]^Vendor[5]) & 0xff;
    response[12]=13;
    return;
    }

if (mode==PLAIN_PASS) //plain pass
    {
    response[6+0]=(PassWord[0]^Vendor[0])&0xff;
    response[6+1]=(PassWord[1]^Vendor[1])&0xff;
    response[6+2]=(PassWord[2]^Vendor[2])&0xff;
    response[6+3]=(PassWord[3]^Vendor[3])&0xff;
    response[6+4]=(PassWord[4]^Vendor[4])&0xff;
    response[6+5]=(PassWord[5]^Vendor[5])&0xff;
    response[12]=13;
    return;
    }
if (mode>PLAIN_PASS) //nothing
    {
    response[6+0]=0xff;
    response[6+1]=0xff;
    response[6+2]=0xff;
    response[6+3]=0xff;
    response[6+4]=0xff;
    response[6+5]=0xff;
    response[12]=13;
    return;
    }
}
/////////////////////////////////////////////////////////////////////////////////        
        
////////////////////////////////////////////////////////////////////////////////////////////////    
static public int round(double dd)
        {
            return (int)(dd + 0.5);
        }
//////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Check if file exists
 * @param str - file name + path
 * @return 
 */
///////////////////////////////////////////////////////////////////////////////        
static public boolean FileExists(String str)        
{
    
String  stra=str;
File f = new File(stra); 

if (f.exists())    return true;
else return false;
}
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/**
 * Get hex value one nibble 
 * @param dta
 * @return 
 */
///////////////////////////////////////////////////////////////////////////////
static public String Hex(int dta)        
{
    
dta=dta&0x0f;
if (dta<10) return String.valueOf(dta);
if (dta==10) return "A";
if (dta==11) return "B";
if (dta==12) return "C";
if (dta==13) return "D";
if (dta==14) return "E";
if (dta==15) return "F";
return "0";
}
/**
 * Convert integer to hex data in string 00-FF
 * @param dta
 * @return 
 */
///////////////////////////////////////////////////////////////////////////////
static public String ConvertAsciHex(int dta)
{
String res="";    
int tempdta=dta;

tempdta&=0x0f;
res=Hex(tempdta);
tempdta=dta>>4;
tempdta&=0x0f;
res=Hex(tempdta)+res;
return res;
}
////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
/*
  */      
        
// Add to log st        
//////////////////////////////////////////////////////////////////////////////////        
    public static void l(String str)    
    {
    //log+=str+"\r\n";
        System.out.println("Out :"+str);    
        System.err.println("Err: "+str);
    }
////////////////////////////////////////////////////////////////////////////////////
//
/** Create eHouse Connection, setting all streams
   send DirectEvents via Ethernet / TCP IP directly to eHouse controllers
 * 
 * @param host - if empty use CommManager or eHouse PC Server Address, otherwise use this parameters
 * @param port - if not 0 then use port 
 * @return 
 */
//
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
public static boolean SendWiFi(String host,int port)    
{

if (EventSize==0) return true;
if (Terminate)   return false;
String IPadr=TCPLocalServer;
int PORT=Integer.parseInt(TCPLocalPort);
  
    if (host.length()>0) IPadr=host;
    if (port>0) PORT=port;

WiFiSendOk=false;    
if (CreateSocket(IPadr,PORT))
    {   //Send OK and close socket
    
    }

 
else CloseSockets();
return false;
}
/////////////////////////////////////////////////////////////////////////////////
//
//
// Close all sockets, free buffers, handler setting to null
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

static void CloseSockets()
{
    

    try
        {
            
       if (ehouseinput!=null) 
            ehouseinput.close();
       ehouseinput=null;
        }
    catch (java.io.IOException iof)   
            {   ehouseinput=null;
                l("Error closing buffered input stream: "+iof.getMessage());
            }
    //ehouseoutput.close();
    try
        {
        if (ehouseout!=null)
            ehouseout.close();
        ehouseout=null;
        }
    catch (java.io.IOException iof)   
        {
        ehouseout=null;
        l("Error closing output stream: "+iof.getMessage());
        }
    try{
        if (ehousein!=null)
        ehousein.close();
        ehousein=null;
        }
    catch (java.io.IOException iof)   
            {
            ehousein=null;
            l("Error closing input stream: "+iof.getMessage());
            }
    try
        {
        if (ehouseconnection!=null)
        ehouseconnection.close();
        ehouseconnection=null;
        }
    catch (java.io.IOException iof)   
            {ehouseconnection=null;
                l("Error closing connection socket: "+iof.getMessage());
            }
    l("Clear Sockets");
    ehouseinput=null;
    ehousein=null;
    ehouseout=null;
    ehouseconnection=null;

}
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////

//
// List Log Data
//
//
/////////////////////////////////////////////////////////////////////////
static public String GetLog()
{
    return log;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
//
//
// Clear Log Data
//
//
////////////////////////////////////////////////////////////////////////////////////
public static void ClearLog()
{
    log="";
}

////////////////////////////////////////////////////////////////////////////////////
//initialize
public static void init()
{
    if (Initialized) return;
   WiFiReadCfg();
    
    
    
Initialized=true;    
}
//
/** Create Socket, Connect, Authorise - Chalange / Response SHORT EVENT SEND PROTOCOL EHouse */
//
//////////////////////////////////////////////////////////////////////////////////////////
    private static boolean CreateSocket(String addr,int port)    
    {
        int i=0;
        char[] TempBuff=new char[255];
        byte[] TempBuff2=new byte[255];
 
        l("Start creation socket connection");              
    try
        {
        InetAddress serverAddr = InetAddress.getByName(addr);
        ehouseconnection = new Socket(serverAddr,port);
        ehouseconnection.setTcpNoDelay(true);
        ehouseconnection.setSoLinger(false, 0);
        ehouseconnection.setSoTimeout(3000);
        ehouseconnection.setKeepAlive(true);
        }
 catch (java.net.UnknownHostException ife)
        {
        l("Error Creating Socket (Unknown Host): "+ife.getMessage());
        return false;
        }
catch (java.io.IOException ioe)    
        {
        l("Error Creating Input Socket (I/O Exception): "+ioe.getMessage());
        return false;
        }
    try    
        {
        ehousein = ehouseconnection.getInputStream();
        }
 catch (java.io.IOException iof)
        {
        l("Error Creating Input Stream (I/O Exception): "+iof.getMessage());
        return false;
        }
 try
       {
        ehouseout = ehouseconnection.getOutputStream ();
        }
 catch (java.io.IOException iof)   
 {
     l("Error Creating output steream (I/O Exception): "+iof.getMessage());     
     return false;
 }
 if (ehouseconnection.isConnected())
 {
 l("connected");
 }
 ehouseinput = new BufferedReader (new InputStreamReader(ehousein));
 
// str="";
 try
    {
 
    if ((ehouseinput.read(TempBuff, 0, 6))==6)
        {   /// readed chalange code from controller
        
        pass=PassCodes;
        i=0;
        //l("Chalange Received from Server");
        response[i] = TempBuff[i]; i++;             //Calculate Response
        response[i] = TempBuff[i]; i++;
        response[i] = TempBuff[i]; i++;
        response[i] = TempBuff[i]; i++;
        response[i] = TempBuff[i]; i++;
        response[i] = TempBuff[i]; i++;
        l("Receive challange: "+hix(response,6));

        MakeChalangeResponse(XOR_PASS);   ///Calculate XORed password for authorisation
        
        for (int m=0;m<13;m++) TempBuff2[m]=(byte)response[m];
        i=13;TempBuff2[i]=(byte)EventSize;response[i]=EventSize;i++;        //attach Event to submit
        
        
        for (int m=0;m<EventSize;m++){TempBuff2[m+14]=(byte)EventToRunByte[m];response[m+14]=EventToRunByte[m];}
        
        
        
        l("Sending Response: "+hix(response,EventSize+14));    
        try
        {
        ehouseout.write(TempBuff2, 0, EventSize+14);   //send size
        ehouseout.flush();
        }
        catch (Exception e)
            {   
             l("Error Sending data: "+e.getMessage());       
             return false;
            }
        TempBuff[0]='\0';
        int Res=0;
        l("Receive response ");
        try
            {
        Res=ehouseinput.read();
            }
        catch (Exception e)
                {
                l("Error Receiving Confirmation: "+e.getMessage());    
                return false;
                }
        l("Received Confirmation:"+ (char)(Res)+" value: "+String.valueOf(Res));    
            {
          
            if (Res==(int)'+')    
                {
                l("Events Send successfully");
                
                ClearEventQueue();              //Clear event queue
                WiFiSendOk=true;                       
                TempBuff[0]=0;        
                
                CloseSockets();                 //close socket
        
                return true;
                }
            else 
                {
                l("Event Not Confirmed");    
                try
                {
                ehouseout.write(0);   ///force closing socket on server side
                
                }
                catch (Exception e)
                {
                    l("error sending closing command: "+e.getMessage());
                }
                CloseSockets();
        
                return false;
                }
        
            }

        }
    else
        {
        l("Wrong Amont of challange data");
        return false;
        }
    }
 catch (java.io.IOException iof)
    { 
        l("Error read the Socket (I/O Exception ): "+iof.getMessage());
        return false;
    }
 }
////////////////////////////////////////////////////////////////////////////////////
    
    
    
/////////////////////////////////////////////////////////////////////////////////////    
/**
 * Delete file
 * @param str  - path+filename
 */    
////////////////////////////////////////////////////////////////////////////////////
public static void delete(String str)
{
    File fl=new File(str);
    fl.delete();
}
////////////////////////////////////////////////////////////////////////////////

/**
 * Read whole file max 100 lines
 * @param str - path+filename
 * @return  array of string
 */
///////////////////////////////////////////////////////////////////////////////////
    public static String[] getfile(String str)
    {
    String[] stttt=new String[260];
    String stra=str;
    int index=0;
    File f = new File(stra);
    
    try
    {
   FileInputStream fileIS = new FileInputStream(f);
   BufferedReader buf = new BufferedReader(new InputStreamReader(fileIS,enc));
   String readString = null; 
      while((readString = buf.readLine())!= null)
        {
        stttt[index]=readString;
        index++;
        stttt[index]=null;
        if (index>259) break;
        }
    } 
    catch (FileNotFoundException e) 
        {
        l("File: " + str+
                             " could not be found on filesystem : "+e.getMessage());    
        }
    catch (UnsupportedEncodingException e)
        {
        l("Exception while reading the file" + e.getMessage());    
        
        }
    catch (IOException e)
        {
        l("Exception while reading the file" + e.getMessage());    

        }
    

return stttt;        
    }
////////////////////////////////////////////////////////////////////////////////    
/**    
 * 
 * Read initial config for application.
 * 
 * 
 */ 
    
static private boolean WiFiReadCfg()
            {
        

        String[] wificfg=new String[20];

        wificfg=getfile(System.getProperty("user.dir")+sep+"wifi.cfg");
        TCPLocalServer=wificfg[0];
        TCPLocalPort=wificfg[1];
        UDPStatusReceptionPort=wificfg[2];
        PassCodes=wificfg[3];
        PassWord[0]=PassCodes.charAt(0);
        PassWord[1]=PassCodes.charAt(1);
        PassWord[2]=PassCodes.charAt(2);
        PassWord[3]=PassCodes.charAt(3);
        PassWord[4]=PassCodes.charAt(4);
        PassWord[5]=PassCodes.charAt(5);
                      
        VendorCodes=wificfg[4];
        eHousePath=wificfg[5];
        OpenRemotePort=wificfg[6];
        try
            {
            OpenRemotePortNr=Integer.parseInt(OpenRemotePort);
            }
        catch (Exception e)
                {
                    l("Wrong open remote port will use default : 4321");
                    OpenRemotePortNr=4321;
                }
        int h=0;
        try
            {
            Vendor[0]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            Vendor[1]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            Vendor[2]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            Vendor[3]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            Vendor[4]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            Vendor[5]=Integer.parseInt(VendorCodes.substring(h,h+1),16)*16+Integer.parseInt(VendorCodes.substring(h+1,h+2),16);h+=2;
            }
       catch(Exception e)
                {
                System.out.print("Exception in vendor code: " + e.getMessage());
                }

return true;
    }
    
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Clear Event Queue to send events
 */
public static void ClearEventQueue()
{
while (EventSize>0)
                  {
                  EventToRunByte[EventSize]=0;
                  EventSize--;
                  }
EventToRun="";
EventSize=0;

}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/** 
 * Convert Event and add to queue from asci hexed value
 *
 * 
 * @param dta - String event in 20bytes of hex value spaces are removed eg. "00 FE 21 00 02 00 00 00 00 00"
 * @return size of queue of events store in cache 
 */
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static int AddAsciiEvent(String dta)
        {
            dta=dta.toUpperCase();
            dta=dta.replaceAll(" ", "");
            dta=dta.replaceAll("\t", "");
            dta+="0000000000000000000000000000";     //just in case if no event become empty event
            int offset=EventSize;
            EventToRunByte[offset+0] = GetHex(dta, 0);
            EventToRunByte[offset+1] = GetHex(dta, 2);
            EventToRunByte[offset+2] = GetHex(dta, 4);
            EventToRunByte[offset+3] = GetHex(dta, 6);
            EventToRunByte[offset+4] = GetHex(dta, 8);
            EventToRunByte[offset+5] = GetHex(dta, 10);
            EventToRunByte[offset+6] = GetHex(dta, 12);
            EventToRunByte[offset+7] = GetHex(dta, 14);
            EventToRunByte[offset+8] = GetHex(dta, 16);
            EventToRunByte[offset+9] = GetHex(dta, 18);
            EventSize=offset+10;
            EventToRun+=dta;
            return EventSize;
            
        }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Convert Event and add to queue from asci hexed value
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
/** get binary data from hex 2 byte - 00-FF */
//        
//
public static int GetHexU(String st,int offset)
        {
        int temp;
        int temp2;
        char[] cst=st.toUpperCase().toCharArray();
                
        if (cst[offset+0] <= '9')
            temp = cst[offset+0] - '0';
        else
            temp = 10+cst[offset+0] - 'A';

        if (cst[offset+1] <= '9')
            temp2 = cst[offset+1] - '0';
        else
            temp2 = 10 + cst[offset + 1] - 'A';
        temp=(temp<<4)+temp2;
        return temp;
        }
/////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//
/** get binary data from hex 2 byte */
//        
//
public static byte GetHex(String st,int offset)
        {
        int temp;
        int temp2;
        char[] cst=st.toUpperCase().toCharArray();
                
        if (cst[offset+0] <= '9')
            temp = cst[offset+0] - '0';
        else
            temp = 10+cst[offset+0] - 'A';

        if (cst[offset+1] <= '9')
            temp2 = cst[offset+1] - '0';
        else
            temp2 = 10 + cst[offset + 1] - 'A';
        temp=(temp<<4)+temp2;
        return (byte) temp;
        }
  //////////////////////////////////////////////////////////////////////////////
  



}
