# (c) Fugro GeoServices, Nelen & Schuurmans. MIT licensed, see LICENSE.rst.
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from datetime import datetime
import SocketServer
import errno
import logging.config
import os
import socket

import psycopg2
import pytz

from ddsc_socket import settings
from ddsc_socket.celery import celery

logger = logging.getLogger(__name__)

BUFSIZE = 1024  # bytes
SQL = "SELECT count(1) FROM lizard_nxt_ipmapping WHERE ip_address = '{}';"

CONNEW = "Client connecting from %s:%d."
CONEND = "Connection with %s:%d lost."
UNAUTH = "Will not serve %s:%d (unauthorized)."


class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):

    def _get_data(self):
        """Return the data send by a client.

        What is currently lacking, is some kind of "EOF" command the client
        can send to signal end-of-file.

        NB: a connection closed by a client does not always result in a
        socket.error. In that case, data is an empty string.

        """
        try:
            self.request.send("ok")
            data = self.request.recv(BUFSIZE)  # BLOCKING
        except socket.error as e:
            if e.errno == errno.ECONNRESET:
                logger.info(CONEND, self.ip, self.port)
            else:
                logger.error(e)
            data = ''
        else:
            if not data:
                logger.info(CONEND, self.ip, self.port)
        finally:
            return data

    def handle(self):

        self.ip, self.port = self.client_address
        logger.info(CONNEW, self.ip, self.port)

        try:
            conn, cur = None, None
            conn = psycopg2.connect(**settings.DATABASE)
            cur = conn.cursor()
            cur.execute(SQL.format(self.ip))
            count = cur.fetchone()[0]
        except Exception as e:
            logger.error(e)
            return
        finally:
            if cur and not cur.closed:
                cur.close()
            if conn and not conn.closed:
                conn.close()

        if count < 1:
            logger.warning(UNAUTH, self.ip, self.port)
            return

        data = self._get_data()
        if not data:
            return

        utc = datetime.now(pytz.utc).isoformat()
        filename = "{}_{}.csv".format(self.ip, utc)
        file_path = os.path.join(settings.DIR, filename)

        # TODO: implement a timeout.
        # TODO: implement a rolling file.

        with open(file_path, 'wb') as f:
            logger.info("Writing data to %s", file_path)
            f.write(data)
            while True:
                data = self._get_data()
                if data:
                    f.write(data)
                else:
                    break

        TASK = "lizard_nxt.tasks.import_socket_timeseries_from_csv"
        logger.info("Sending task to Celery: %s", TASK)
        celery.send_task(TASK, args=[file_path])


class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass


def main():
    logging.config.dictConfig(settings.LOGGING)
    server = ThreadedTCPServer(
        (settings.HOST, settings.PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address
    logger.info("Server listening on %s:%d.", ip, port)
    server.serve_forever()  # Interrupt with Ctrl-C.

if __name__ == "__main__":
    main()

@@ -347,21 +347,51 @@
 import s
-ocket
+hutil%0Aimport socket%0Aimport tempfile
 %0A%0Aimport
@@ -752,16 +752,154 @@
 zed).%22%0A%0A
+# Isilon storage does not support colons in filenames.%0A# For that reason, ISO 8601's basic format is used.%0AISO8601 = '%25Y%25m%25dT%25H%25M%25S.%25fZ'%0A%0A
 %0Aclass T
@@ -2478,32 +2478,211 @@
         return%0A%0A
+        # Firstly, data is written to a temporary file. Only when all data is%0A        # received, it is copied to shared storage, which is accessible by a%0A        # task server.%0A%0A
         utc = da
@@ -2706,18 +2706,24 @@
 tc).
-isoformat(
+strftime(ISO8601
 )%0A  
@@ -2780,25 +2780,19 @@
         
-file_path
+dst
  = os.pa
@@ -2921,28 +2921,36 @@
 ith 
-open(file_path, 'wb'
+tempfile.NamedTemporaryFile(
 ) as
@@ -2972,20 +2972,21 @@
  logger.
-info
+debug
 (%22Writin
@@ -3001,24 +3001,21 @@
 o %25s%22, f
-ile_path
+.name
 )%0A      
@@ -3204,16 +3204,130 @@
    break
+%0A            f.flush()%0A            logger.info(%22Copying data to %25s%22, dst)%0A            shutil.copyfile(f.name, dst)
 %0A%0A      
@@ -3485,17 +3485,11 @@
 gs=%5B
-file_path
+dst
 %5D)%0A%0A

Support Isilon storage.