#!/usr/bin/python
#
# Copyright (c) 2011 Vince Durham
# Copyright (c) 2013-2014 The Blakecoin developers
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING.
#

import logging
import argparse
import os
import sys
import traceback
import json
import base64
from binascii import a2b_hex, b2a_hex
import socket

from datetime import datetime

from twisted.internet import defer, reactor, threads
from twisted.web import server, resource
from twisted.internet.error import ConnectionRefusedError
import twisted.internet.error
from urlparse import urlsplit
import httplib
import thread

try:
    from merkletree import MerkleTree
    have_merkletree = True
except ImportError:
    have_merkletree = False
    class MerkleTree:
        def __init__(self, L, detailed=False):
            self.detail = L

__version__ = '1.2.3'

'''
merge-mine-proxy

Run behind Eloipool for a set of auxiliary chains.

Output is in the form:

2014-05-05T00:00:00,solve,=,1,HASH

Where the fields are:

    * UTC date and time in ISO format
    * The word "solve"
    * 1 if the proof of work was accepted by each aux chain
    * extra ,1 per aux chain e.g solve,=,1,1,HASH
    * HASH parent block hash

'''

AUX_UPDATE_INTERVAL = 5
MERKLE_TREES_TO_KEEP = 240

logger = logging.getLogger('merged-mine-proxy')
logger.setLevel(logging.DEBUG)

def reverse_chunks(s, l):
    return ''.join(reversed([s[x:x+l] for x in xrange(0, len(s), l)]))

def getresponse(http, path, postdata, headers):
    http.request('POST', path, postdata, headers)
    return http.getresponse().read()

class Error(Exception):
    def __init__(self, code, message, data=''):
        if not isinstance(code, int):
            raise TypeError('code must be an int')
        if not isinstance(message, unicode):
            raise TypeError('message must be a unicode')
        self._code, self._message, self._data = code, message, data
    def __str__(self):
        return '%i %s %r' % (self._code, self._message, self._data)
    def _to_obj(self):
        return {
            'code': self._code,
            'message': self._message,
            'data': self._data,
        }

class Proxy(object):
    def __init__(self, url):
        (schema, netloc, path, query, fragment) = urlsplit(url)
        auth = None
        if netloc.find('@') >= 0:
            (auth, netloc) = netloc.split("@")
        if path == "":
            path = "/"
        self._url = "%s://%s%s" % (schema, netloc, path)
        self._path = path
        self._auth = auth
        self._netloc = netloc
        self._http = None
    
    def callRemote(self, method, *params):
        try:
            if self._http is None:
                (host, port) = self._netloc.split(":")
                self._http = httplib.HTTPConnection(host, port)
                try:
                    self._http.connect()
                except socket.error:
                    raise httplib.HTTPException()

            id_ = 0
            
            headers = {
                'Content-Type': 'text/json',
            }
            if self._auth is not None:
                headers['Authorization'] = 'Basic ' + base64.b64encode(self._auth)
            resp = None

            postdata=json.dumps({
                'jsonrpc': '2.0',
                'method': method,
                'params': params,
                'id': id_,
            })

            content = getresponse(self._http, self._path, postdata, headers)

            resp = json.loads(content)
            
            if resp['id'] != id_:
                raise ValueError('invalid id')
            if 'error' in resp and resp['error'] is not None: