# -*- coding: utf-8 -*-

import json
import threading
import os
import time
import mats
import sys
import requests
import traceback
import re
from util import debug, error

class MatsLoader(threading.Thread):
    """
    Fire and forget loader for materials - will queue a 'mats' event or 
    an 'error' event if the load fails. Automatically runs as a daemon
    """
    def __init__(self, filename, queue):
        """
        filename is the file to async load
        queue is the queue to report the results into
        """
        threading.Thread.__init__(self)
        self.queue = queue
        self.filename = filename
        self.daemon = True

    def run(self):
        try:
            m = mats.Materials(self.filename)
            self.queue.put( { 'mats': m._materials } )
        except:
            self.queue.put( { 'error': 'Failed to load materials ' + str(sys.exc_info()[0]) } )

class MatsLoaderRemote(threading.Thread):
    """
    Fire and forget loader for materials - will queue a 'mats' event or 
    an 'error' event if the load fails. Automatically runs as a daemon
    """
    def __init__(self, filename, queue):
        """
        filename is the cache file - we only read the remote file 
        if the cache is old (or missing)
        queue is the queue to report the results into
    
        """
        threading.Thread.__init__(self)
        self.filename = filename
        self.queue = queue
        self.daemon = True
        self.integerRe = re.compile(r'^-?\d+$')
        self.floatRe = re.compile(r'^-?\d+(\.\d+)?$')
        self.arrayRe = re.compile(r'^\[.*\]$')

    def need_refresh(self):
        """
        Returns True if the local cache needs a refresh. 
        """
        if not os.path.exists(self.filename):
            return True

        mtime = os.path.getmtime(self.filename)
        now = time.time()
        return mtime < now - 24 * 3600 # Daily update

    def array_splitter(self, value):
        return [ x[1:-1] for x in value[1:-1].split(", ") ]

    def detect(self, value):
        """
        Looks at a data value and converts into an appropriate type
        (maybe should look at using ast instead)
        """
        if self.integerRe.match(value):
            return int(value)
        elif self.floatRe.match(value):
            return float(value)
        elif self.arrayRe.match(value):
            return self.array_splitter(value)
        else:
            return value

    def parse(self, text):
        """
        Parse a string field containing all the data ina TSV
        into an array of dicts. Mainly split out so we can test
        """

        lines = text.replace("\r", "").split("\n")
        fields = lines[0].split("\t")
        res = []
        for entry in lines[1:]:
            values = entry.split("\t")
            if len(values) < len(fields):
                continue
            v = {}
            for k in range(0, len(fields)):
                v[fields[k]] = self.detect(values[k])
            res.append(v)
        return res

    def run(self):
        try:
            if self.need_refresh():
                r = requests.get("https://docs.google.com/spreadsheets/u/0/d/1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4/export?format=tsv&id=1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4&gid=0")
               
                res = self.parse(r.text)

                if res:
                    with open(self.filename, "wt") as cache_file:
                        json.dump(res, cache_file)
                     
                    self.queue.put( { 'mats': res } )
                    debug("Async remote mats loader from tsv is completed {} entries".format(len(res)))
                else:
                    error("Async remote mats loader failed - zero records")
            else:
                with open(self.filename, "rt") as cache_file:
                    res = json.load(cache_file)
                    self.queue.put( { 'mats': res } )
                    debug("loader from cache is completed {} entries".format(len(res)))

        except:
            self.queue.put( { 'error': 'Failed to load tsv materials ' + str(sys.exc_info()[0]) + ' ' + traceback.format_exc() } )


[((24, 8, 24, 39), 'threading.Thread.__init__', 'threading.Thread.__init__', ({(24, 34, 24, 38): 'self'}, {}), '(self)', False, 'import threading\n'), ((48, 8, 48, 39), 'threading.Thread.__init__', 'threading.Thread.__init__', ({(48, 34, 48, 38): 'self'}, {}), '(self)', False, 'import threading\n'), ((52, 25, 52, 47), 're.compile', 're.compile', ({(52, 36, 52, 46): '"""^-?\\\\d+$"""'}, {}), "('^-?\\\\d+$')", False, 'import re\n'), ((53, 23, 53, 53), 're.compile', 're.compile', ({(53, 34, 53, 52): '"""^-?\\\\d+(\\\\.\\\\d+)?$"""'}, {}), "('^-?\\\\d+(\\\\.\\\\d+)?$')", False, 'import re\n'), ((54, 23, 54, 46), 're.compile', 're.compile', ({(54, 34, 54, 45): '"""^\\\\[.*\\\\]$"""'}, {}), "('^\\\\[.*\\\\]$')", False, 'import re\n'), ((63, 16, 63, 47), 'os.path.getmtime', 'os.path.getmtime', ({(63, 33, 63, 46): 'self.filename'}, {}), '(self.filename)', False, 'import os\n'), ((64, 14, 64, 25), 'time.time', 'time.time', ({}, {}), '()', False, 'import time\n'), ((31, 16, 31, 45), 'mats.Materials', 'mats.Materials', ({(31, 31, 31, 44): 'self.filename'}, {}), '(self.filename)', False, 'import mats\n'), ((60, 15, 60, 44), 'os.path.exists', 'os.path.exists', ({(60, 30, 60, 43): 'self.filename'}, {}), '(self.filename)', False, 'import os\n'), ((106, 20, 106, 195), 'requests.get', 'requests.get', ({(106, 33, 106, 194): '"""https://docs.google.com/spreadsheets/u/0/d/1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4/export?format=tsv&id=1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4&gid=0"""'}, {}), "(\n    'https://docs.google.com/spreadsheets/u/0/d/1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4/export?format=tsv&id=1g0y7inyvQopJ93jP5YIu3n0veX0ng8DraJXAvZk6pS4&gid=0'\n    )", False, 'import requests\n'), ((117, 20, 117, 75), 'util.error', 'error', ({(117, 26, 117, 74): '"""Async remote mats loader failed - zero records"""'}, {}), "('Async remote mats loader failed - zero records')", False, 'from util import debug, error\n'), ((120, 26, 120, 47), 'json.load', 'json.load', ({(120, 36, 120, 46): 'cache_file'}, {}), '(cache_file)', False, 'import json\n'), ((112, 24, 112, 50), 'json.dump', 'json.dump', ({(112, 34, 112, 37): 'res', (112, 39, 112, 49): 'cache_file'}, {}), '(res, cache_file)', False, 'import json\n'), ((125, 104, 125, 126), 'traceback.format_exc', 'traceback.format_exc', ({}, {}), '()', False, 'import traceback\n'), ((34, 73, 34, 87), 'sys.exc_info', 'sys.exc_info', ({}, {}), '()', False, 'import sys\n'), ((125, 77, 125, 91), 'sys.exc_info', 'sys.exc_info', ({}, {}), '()', False, 'import sys\n')]