"""
PLC 1
"""

from minicps.devices import PLC
from threading import Thread
from utils import *
from random import *

import json
import select
import socket
import time
import logging
import signal
import sys

MV101 = ('MV101', 1)
LIT101 = ('LIT101', 1)
P101 = ('P101', 1)

LIT301 = ('LIT301', 3)

SENSOR_ADDR = IP['lit101']
IDS_ADDR = IP['ids101']
PLC301_ADDR = IP['plc301']

class Lit301Socket(Thread):
    """ Class that receives water level from the water_tank.py  """

    def __init__(self, plc_object):        
        Thread.__init__(self)
        self.plc = plc_object

    def run(self):
        self.sock = socket.socket()     # Create a socket object    
        self.sock.bind((IP['plc101-HMI'] , 8754 ))
        self.sock.listen(5)

        while True:
            try:
                client, addr = self.sock.accept()
                data = client.recv(4096)                                                # Get data from the client
                message_dict = eval(json.loads(data))
                lit301 = float(message_dict['Variable'])

                if lit301 >= LIT_301_M['HH'] :
                    self.send_message(IP['p101'], 7842 , 0)

                elif lit301 >= LIT_301_M['H']:
                    self.send_message(IP['p101'], 7842 , 0)

                elif lit301 <= LIT_301_M['L']:
                    self.send_message(IP['p101'], 7842 , 1)

                elif lit301 <= LIT_301_M['LL']:
                    self.send_message(IP['p101'], 7842 , 1)
            except KeyboardInterrupt:
                print "\nCtrl+C was hitten, stopping server"
                client.close()
                break


    def send_message(self, ipaddr, port, message):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ipaddr, port))

        msg_dict = dict.fromkeys(['Type', 'Variable'])
        msg_dict['Type'] = "Report"
        msg_dict['Variable'] = message
        message = json.dumps(str(msg_dict))

        try:
            ready_to_read, ready_to_write, in_error = select.select([sock, ], [sock, ], [], 5)
        except:
            print "Socket error"
            return
        if(ready_to_write > 0):
            sock.send(message)
        sock.close()

class HMISocket(Thread):
    """ Class that responds to the POLL command from HMI """
    def __init__(self, plc_object):
        Thread.__init__(self)
        self.plc = plc_object
        self.lit101 = 0.0

    def run(self):
        self.sock = socket.socket()
        self.sock.connect((IP['HMI'], int(PORTS['hmi_poll_port'])))
        data = ''
        while True:
            while data=='':
                data = self.sock.recv(100)
                time.sleep(1)
                if data == "POLL":
                    self.sock.send("LIT101: "+str(self.lit101))
                data = ''
    def setLit101(self, val):
        self.lit101 = val

class IdsSocket(Thread):
    """ Class that receives water level from the water_tank.py  """

    def __init__(self, plc_object):        
        Thread.__init__(self)
        self.plc = plc_object

    def run(self):
        self.sock = socket.socket()     # Create a socket object    
        self.sock.bind((IP['plc101'] , 4234 ))
        self.sock.listen(5)

        while True:
            try:
                client, addr = self.sock.accept()
                data = client.recv(4096)                                                # Get data from the client
                message_dict = eval(json.loads(data))
                message_type = message_dict['Type']

                if message_type == "Report":
                    lit101 = float(message_dict['Variable'])
                    self.plc.lit101_ids101 = lit101

                elif message_type == "Intrussion":
                    self.plc.intrussion = message_dict['Variable']
            except KeyboardInterrupt:
                print "\nCtrl+C was hitten, stopping server"
                client.close()
                break

    def send_message(self, ipaddr, port, message):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ipaddr, port))

        msg_dict = dict.fromkeys(['Type', 'Variable'])
        msg_dict['Type'] = "Report"
        msg_dict['Variable'] = message
        message = json.dumps(str(msg_dict))

        try:
            ready_to_read, ready_to_write, in_error = select.select([sock, ], [sock, ], [], 5)
        except:
            print "Socket error"
            return
        if(ready_to_write > 0):
            sock.send(message)
        sock.close()        

class PLC101(PLC):

    def sigint_handler(self, sig, frame):
        print "I received a SIGINT!"
        sys.exit(0)

    def pre_loop(self, sleep=0.1):
        signal.signal(signal.SIGINT, self.sigint_handler)
        signal.signal(signal.SIGTERM, self.sigint_handler)

    def main_loop(self):
        """plc1 main loop.
            - reads sensors value
            - drives actuators according to the control strategy
            - updates its enip server
        """

        print 'DEBUG: swat-s1 plc1 enters main_loop.'

        lit301socket = Lit301Socket(self)
        lit301socket.start()
        self.count = 0
        self.lit101_intrussion = False
        self.lit101_ids101 = 0.0
        backup = IdsSocket(self)
        backup.start()

        while True:
            try:
                if self.lit101_intrussion == False:
                    lit101 = float(self.receive(LIT101, SENSOR_ADDR))
                else:
                    lit101 = self.lit101_ids101

                if lit101 >= LIT_101_M['HH']:
                    self.send(MV101, 0, IP['plc101'])

                elif lit101 >= LIT_101_M['H']:
                    self.send(MV101, 0, IP['plc101'])

                elif lit101 <= LIT_101_M['LL']:
                    self.send(MV101, 1, IP['plc101'])

                elif lit101 <= LIT_101_M['L']:
                    self.send(MV101, 1, IP['plc101'])

            except Exception as e:
                print e
                print "Switching to backup"
                break

if __name__ == "__main__":
    plc101 = PLC101(name='plc101',state=STATE,protocol=PLC101_PROTOCOL,memory=GENERIC_DATA,disk=GENERIC_DATA)