#!/usr/bin/env python

"""
:author: Vladan S
:copyright: D-Logic   http://www.d-logic.net
 
"""

__program_version = '4.0.7.11'

"""
  - added local ldev parametar when call DoPOST();
   
  
"""

import cgi
import os
import posixpath
import requests
import shutil
import signal
import sys
import threading
import time
import urllib2
import urllib

from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from SocketServer import ThreadingMixIn

from ctypes import *
from platform import platform
from socket import *
from urlparse import parse_qs
from urlparse import urlparse
from mimetypes import types_map

import traceback

from StringIO import StringIO

from constants import *
from shell.ais_shell import *

global edit_time


def AisHttpGetProgramVersion():
    return 'App name  : {0}  : {1}'.format(AIS_HTTP, __program_version)


def http_request(path, post_attrib, time_out=20):
    try:
        req = urllib2.Request(path, post_attrib)
        req.add_header("Content-type", "application/x-www-form-urlencoded")
        page = urllib2.urlopen(req, timeout=time_out)
        return page.read(), page.code
    except urllib2.URLError, urlExc:
        return '', urlExc.reason
    except urllib2.HTTPError, httpExc:
        return '', httpExc.code


class GetHandler(BaseHTTPRequestHandler):
    global url_query

    def WriteFile(self, path):
        '''Write any file to client
           :param path: self.path-> path to the file 
        '''
        if self.path == '/':
            self.path = path
        fname, ext = os.path.splitext(self.path)
        fname = self.path.lstrip("/")
        with open(os.path.join(os.sep, fname)) as t:
            self.send_response(200)
            self.send_header('Content-type', types_map[ext])
            self.end_headers()
            self.wfile.write(t.read())
            t.close()
        return

    def list_dir(self, path):
        try:
            listdirs = os.listdir(path)
        except os.error:
            self.send_error(404)
            return None
        f = StringIO()
        f.write('<!DOCTYPE html>')
        f.write("<html>\n<title>Directory listing</title>\n")
        f.write("<body>\n<h2>Directory listing for {0}</h2>\n".format(path))
        f.write("<hr>\n<ul>\n")
        for name in listdirs:
            fullname = os.path.join(path, name)
            displayname = linkname = name
            if os.path.isdir(fullname):
                displayname = name + "/"
                linkname = name + "/"
            if os.path.islink(fullname):
                displayname = name + "@"
            f.write('<li><a href="%s">%s</a>\n' % (urllib.quote(linkname), cgi.escape(displayname)))
        f.write("</ul>\n<hr>\n</body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        encoding = sys.getfilesystemencoding()
        self.send_header("Content-type", "text/html; charset=%s" % encoding)
        self.send_header("Content-Length", str(length))
        self.end_headers()
        return f

    def do_HEAD(self):
        f = self.send_head()
        if f:
            f.close()

    def translate_path(self, path):
        """Translate a /-separated PATH to the local filename syntax.
           :param path:current path to translate path
           :return: translated path
        """

        path = path.split('?', 1)[0]
        path = path.split('#', 1)[0]
        path = posixpath.normpath(urllib.unquote(path))
        words = path.split('/')
        words = filter(None, words)
        path = os.getcwd()
        for word in words:
            drive, word = os.path.splitdrive(word)
            head, word = os.path.split(word)
            if word in (os.curdir, os.pardir): continue
            path = os.path.join(path, word)
        return path

    def script_name(self):
        if not os.path.basename(sys.argv[0]) == AIS_HTTP:
            return [HTML_OTHER_INIT_SCRIPT, HTM_OTHER_INIT_SCRIPT]
        return [HTML_INIT_SCRIPT, HTM_INIT_SCRIPT]

    def send_head(self):
        try:
            path = self.translate_path(self.path)
            f = None
            if os.path.isdir(path):
                scripts = self.script_name()
                for index in scripts:
                    index = os.path.join(os.path.join(os.getcwd(), HTML_FOLDER, index))
                    if os.path.exists(index):
                        path = index
                        break
                else:
                    return self.list_dir(path)
            try:
                f = open(path, 'rb')
            except IOError:
                self.send_error(404, 'File Not Found: %s' % self.path)
                return None

            fname, ext = os.path.splitext(path)
            self.send_response(200)
            self.send_header("Content-type", types_map[ext])
            fs = os.fstat(f.fileno())
            self.send_header("Content-Length", str(fs[6]))
            self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
            self.end_headers()
            return f
        except IOError:
            self.send_error(404, 'File Not Found: %s' % self.path)
        return None

    def SetHeaders(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        try:
            f = self.send_head()
            if f:
                shutil.copyfileobj(f, self.wfile)
                f.close()
        except IOError:
            self.send_error(404, 'File Not Found: %s' % self.path)
        return

    def do_POST(self):
        try:
            # dev = DEV_HND
            global url_query, edit_list_choise
            global start_index, end_index
            global start_time, end_time

            ldev = device_list.S_DEVICE

            ctype, pdict = cgi.parse_header(self.headers['content-type'])

            if ctype == 'multipart/form-data':
                pq = cgi.parse_multipart(self.rfile, pdict)

            elif ctype == 'application/x-www-form-urlencoded':
                length = int(self.headers['content-length'])
                pq = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)

            else:
                pq = {}

            f = ''.join(pq[FUNCTION] if pq.has_key(FUNCTION) else '')
            device = ''.join(pq[DEVICE] if pq.has_key(DEVICE) else '')

            if f == 'q':
                self.wfile.write(GetListInformation())

            if f == 'v':
                self.wfile.write("AIS_GetDLLVersion() >> %s\n" % AISGetLibraryVersionStr())

            if f in ('x', 'X'):
                self.wfile.write("\nServer stopped !\nClose program !\n")
                shut_event.set()
                if sys.platform.startswith('linux'):
                    os.system('pkill -9 python')
                    os.kill(os.getpid(), signal.SIGINT)
                elif sys.platform.startswith('win'):
                    os._exit(0)

            try:
                if device is '':
                    ldev.hnd = HND_LIST[0]
                elif int(device) > len(HND_LIST):
                    self.wfile.write("\ndev[%s]NO DEVICE FOUND (or resource busy)\n" % device)
                    return
                else:
                    ldev.hnd = HND_LIST[int(device, 10) - 1]
            except Exception as exc:
                print exc

            if pq.has_key(DEVICE_TYPE):
                device_type = ''.join(pq[DEVICE_TYPE])

            if pq.has_key(DEVICE_ID):
                device_id = ''.join(pq[DEVICE_ID])

            if pq.has_key(EDIT_LIST):
                edit_list_choise = ''.join(pq[EDIT_LIST])

            if f == 'R':
                try:
                    self.wfile.write(AISRestart(ldev))
                except TypeError:
                    self.wfile.write('')

            if f == 'D':
                try:
                    self.wfile.write(AISDestroy(ldev))
                except TypeError:
                    self.wfile.write('')

            if f == 'Q':
                if edit_list_choise == AVAILABLE_DEVICES:
                    self.wfile.write(edit_device_list(1))
                elif edit_list_choise == ACTUAL_LIST:
                    self.wfile.write(edit_device_list(2))
                elif edit_list_choise == CLEAR_LIST:
                    self.wfile.write(edit_device_list(3))
                elif edit_list_choise == ADD_DEVICE:
                    self.wfile.write("AIS_List_AddDevicesForCheck() ...\n")
                    if device_type == '' or device_id == '':
                        self.wfile.write("You must enter values in the relevant fields !")
                        return
                    else:
                        self.wfile.write(
                            edit_device_list(4, "AIS_List_AddDeviceForCheck", int(device_type), int(device_id)))
                elif edit_list_choise == ERASE_DEVICE:
                    self.wfile.write("AIS_List_EraseDeviceForCheck()...\n")
                    self.wfile.write(
                        edit_device_list(5, "AIS_List_EraseDeviceForCheck", int(device_type), int(device_id)))
                else:
                    self.wfile.write("")

            if pq.has_key(START_INDEX) and pq.has_key(END_INDEX):
                start_index = (''.join(pq[START_INDEX]))
                end_index = (''.join(pq[END_INDEX]))

            if pq.has_key(START_TIME) or pq.has_key(END_TIME):
                start_time = (''.join(pq[START_TIME]))
                end_time = (''.join(pq[END_TIME]))

            if pq.has_key(WHITE_LIST_WRITE):
                white_list_write = ''.join(pq[WHITE_LIST_WRITE])

            if pq.has_key(BLACK_LIST_WRITE):
                black_list_write = ''.join(pq[BLACK_LIST_WRITE])

            if pq.has_key(DEFAULT_APP_PASS):
                set_def_pass = ''.join(pq[DEFAULT_APP_PASS])

            if pq.has_key(NEW_PASS):
                new_pass = ''.join(pq[NEW_PASS])

            if pq.has_key(UNREAD_LOG):
                get_unread_log = (''.join(pq[UNREAD_LOG]))

            if pq.has_key(LIGHTS):
                lights_choise = ''.join(pq[LIGHTS])

            if f == 'o':
                try:
                    self.wfile.write(AISOpen(ldev))
                except TypeError:
                    self.wfile.write('')

            elif f == 'c':
                try:
                    self.wfile.write(AISClose(ldev))
                except TypeError:
                    self.wfile.write('')

            if f == 'd':
                try:
                    res, count = AISUpdateAndGetCount()
                    self.wfile.write(' COUNT >> {0} {1}'.format(count, wr_status('', res)))
                except WindowsError:
                    pass

            elif f == 't':
                gettime, currtimes = AISGetTime(ldev)
                self.wfile.write(gettime)

            elif f == 'T':
                self.wfile.write(sys_get_timezone_info() + "\n")
                self.wfile.write(AISSetTime(ldev))

            elif f == 'r':
                pass
                rt = ''.join(pq[RTE])
                if rt.strip() == '':
                    self.wfile.write('Must enter value for seconds !')
                    return
                else:
                    try:
                        seconds = int(rt)
                        stop_time = c_uint64()
                        stop_time = time.time() + seconds  # 10
                        # dev = DEV_HND
                        self.wfile.write("Wait for RTE for %d sec ...\n" % seconds)
                        while time.ctime(time.time()) < time.ctime(stop_time):
                            for hnd in HND_LIST:
                                ldev.hnd = hnd
                                r, rte = MainLoop(ldev)
                                if rte is not None:
                                    self.wfile.write(rte)
                            time.sleep(THD_SLEEP)
                        self.wfile.write("End RTE listen")
                    except Exception as vError:
                        self.wfile.write(vError)
                        return

            elif f == 'l':
                self.wfile.write(log_get(ldev))

            elif f == 'n':
                if start_index and end_index:
                    self.wfile.write(log_by_index(int(start_index), int(end_index), ldev))
                else:
                    self.wfile.write('You must enter values for START INDEX  and END INDEX !')

            elif f == 'N':
                if start_time and end_time:
                    self.wfile.write(log_by_time(int(start_time), int(end_time), ldev))
                else:
                    self.wfile.write('You must enter values for START TIME  and END TIME !')

            elif f == 'u':
                get_unread_log = int(get_unread_log)
                self.wfile.write(get_unread_log_one(get_unread_log, ldev))

            elif f == 'w':
                self.wfile.write(whitelist_read(ldev))

            elif f == 'b':
                self.wfile.write(blacklist_read(ldev))

            elif f == 'W':
                self.wfile.write(whitelist_write(white_list_write, ldev))

            elif f == 'B':
                self.wfile.write(blacklist_write(black_list_write, ldev))

            elif f == 'L':
                self.wfile.write(TestLights(lights_choise, ldev))

            elif f == 'g':
                self.wfile.write(get_io_state(ldev))
            elif f == 'G':
                self.wfile.write(lock_open(ldev))
            elif f == 'y':
                self.wfile.write(relay_toogle(ldev))

            elif f == 'P':
                global PASS
                self.wfile.write("Actual application password is :%s\n" % PASS)
                if len(set_def_pass) == 0:
                    self.wfile.write("Patch - new pass = default pass\n")
                    set_def_pass = PASS
                PASS = set_def_pass
                self.wfile.write(password_set_default(set_def_pass))

            elif f == 'p':
                global PASS
                new_pass = ''
                self.wfile.write("Old password is actual application password: %s\n" % PASS)
                self.wfile.write("New password for units ( and application ): %s\n" % new_pass)
                if len(new_pass) == 0:
                    self.wfile.write("Patch - new pass = default pass\n")
                    new_pass = PASS
                self.wfile.write("Try set new password for units= %s\n" % (new_pass))
                self.wfile.write(password_change(new_pass, ldev))

            elif f == 'f':
                self.wfile.write(AISGetVersion(ldev))

            elif f == 'i':
                self.wfile.write(AISGetTime(ldev)[0])
                self.wfile.write(sys_get_timezone_info() + "\n")
                self.wfile.write(AISGetVersion(ldev))

            elif f == 'E':
                self.wfile.write(ee_lock(ldev))

            elif f == 'e':
                self.wfile.write(ee_unlock(ldev))

            elif f == 'F':
                try:
                    if pq.has_key('fw_file'):
                        fw_file_name = ''.join(pq['fw_file_name'])
                        with open(fw_file_name, 'wb') as out:
                            out.write(''.join(pq['fw_file']))
                        out.close()
                        self.wfile.write(fw_update(ldev, fw_name=fw_file_name))
                        os.remove(fw_file_name)
                except Exception as exc:
                    self.wfile.write("ERROR: %s" % exc)

            elif f == 's':
                try:
                    if pq[CONFIG_FILE_READ] is not None:
                        conf_file_rd = ''.join(pq[CONFIG_FILE_READ])
                        self.wfile.write(config_file_rd(fname=conf_file_rd))

                        from socket import gethostname, gethostbyname
                        ip = gethostbyname(gethostname())

                        with open(conf_file_rd + '.config', 'wb') as out:
                            http_request(ip + ":" + str(HTTP_SERVER_PORT), out.write(conf_file_rd + '.config'))
                except Exception as exc:
                    self.wfile.write("ERROR: %s" % exc)

            elif f == 'S':
                try:
                    if pq[CONFIG_FILE_WR_NAME] is not None:
                        confFileNameWR = ''.join(pq[CONFIG_FILE_WR_NAME])
                        confFileWR = ''.join(pq[CONFIG_FILE_WRITE])
                        with open(confFileNameWR, 'wb') as out:
                            out.write(confFileWR)
                        self.wfile.write(config_file_wr(fname=confFileNameWR))
                    else:
                        self.wfile.write('NO FILE')
                except Exception as exc:
                    self.wfile.write("ERROR: %s" % exc)

            if not os.path.basename(sys.argv[0]) == AIS_HTTP:
                try:
                    from ais_readers_main_process import DoPOST
                    writefile, content = DoPOST(f, ldev, **pq)
                    if writefile:
                        return self.WriteFile(content)
                    else:
                        # self.SetHeaders()
                        return self.wfile.write(content)
                except Exception as exc:
                    self.wfile.write(exc)

            return self.wfile.write('\n')

        except Exception as error_mess:
            self.wfile.write(traceback.print_exc())


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""


def RunAll():
    serv = threading.Thread(target=handler_server)
    serv.start()
    while True:
        try:
            if serv.isAlive():
                serv.join(timeout=SERV_JOIN)
        except (KeyboardInterrupt, SystemExit, Exception) as e:
            httpd.server_close()
            print '\nServer stopped\nProgram close', e
            shut_event.set()
            if sys.platform.startswith('linux'):
                os.system('pkill -9 python')
            elif sys.platform.startswith('win'):
                sys.exit(0)
            break


def handler_server():
    global httpd
    while not shut_event.is_set():
        my_lock.acquire()
        httpd.handle_request()
        my_lock.release()
        time.sleep(THD_SLEEP)


def init():
    print AISGetLibraryVersionStr()
    global httpd
    list_device()
    httpd = HTTPServer((HTTP_SERVER_NAME, HTTP_SERVER_PORT), GetHandler)
    httpd.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    RunAll()


my_lock = threading.Lock()
shut_event = threading.Event()

if __name__ == '__main__':
    global httpd
    init()