#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Search and replaces BTC addresses and private keys in WIF to Zclassic variant"""

import click
import imp
import re

imp.load_module('lib', *imp.find_module('../../lib'))

from lib.bitcoin import (b58_address_to_hash160, hash160_to_b58_address,
                         base_decode, serialize_privkey, DecodeBase58Check,
                         SCRIPT_TYPES)
from lib.util import inv_dict, to_bytes
from lib.constants import net


ADDR_PATTERN = re.compile(
    '([123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
    'abcdefghijkmnopqrstuvwxyz]{20,180})')


def b58_address_to_hash160_btc(addr):
    addr = to_bytes(addr, 'ascii')
    _bytes = base_decode(addr, 25, base=58)
    return _bytes[0], _bytes[1:21]


def deserialize_btc_priv(val):
    try:
        vch = DecodeBase58Check(val)
    except:
        return None, None, None

    if vch[0] != 0x80:
        return None, None, None

    if len(vch) not in [33, 34]:
        return None, None, None

    txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - 0x80]
    compressed = len(vch) == 34

    return txin_type, vch[1:33], compressed


CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.command(context_settings=CONTEXT_SETTINGS)
@click.option('-i', '--input-file', required=True,
              help='Input file')
@click.option('-n', '--dry-run', is_flag=True,
              help='Only show what will be changed')
@click.option('-o', '--output-file',
              help='Output file')
@click.option('-p', '--inplace', is_flag=True,
              help='Replace data inplace')
def main(**kwargs):
    input_file = kwargs.pop('input_file')
    output_file = kwargs.pop('output_file', None)
    inplace = kwargs.pop('inplace', False)
    dry_run = kwargs.pop('dry_run', False)
    if inplace:
        output_file = input_file

    olines = []
    total_sub = 0
    for ln, l in enumerate(open(input_file, 'r').read().splitlines()):
        pos = 0
        ol = ''

        while pos < len(l):
            m = ADDR_PATTERN.search(l, pos)
            if not m:
                ol += l[pos:]
                break

            ol += l[pos:m.start()]
            val = m.group()

            try:
                addrtype, h = b58_address_to_hash160_btc(val)
            except:
                h = None

            if h and addrtype == 0:
                new_val = hash160_to_b58_address(h, net.ADDRTYPE_P2PKH)
                total_sub +=1
            elif h and addrtype == 5:
                new_val = hash160_to_b58_address(h, net.ADDRTYPE_P2SH)
                total_sub +=1
            else:
                new_val = None


            if not new_val:
                try:
                    txin_type, privkey, compressed = deserialize_btc_priv(val)
                except Exception as e:
                    privkey = None

                if privkey:
                    new_val = serialize_privkey(privkey, compressed, txin_type,
                                                internal_use=True)

            if dry_run and new_val:
                print('line %s, col %s: %s => %s' % (
                    ln, m.start(), val, new_val
                ))

            ol += new_val if new_val else val
            pos = m.end()

        olines.append(ol)

    out = '\n'.join(olines)
    if not output_file:
        print(out)
    elif not dry_run:
        with open(output_file, 'w') as wfd:
            wfd.write('%s\n' % out)
    else:
        print('Total sub count:', total_sub)


if __name__ == '__main__':
    main()
