// tslint:disable:no-unused-variable
// tslint:disable:no-use-before-define
import {BigNumber} from "bignumber.js"
import {TokenTrc20} from "../TokenTrc20";
import CoinDetail from "../CoinDetail";

export interface Balancer {
    [holder_address: string]: number;
}

export interface Spending {
    [holder_address: string]: Balancer;
}

export interface Unlimited {
    [holder_address: string]: boolean;
}


export interface TronLinkEventCaller {
    signer(payload: TronLinkTunnelMessage): boolean;

    reply(payload: TronLinkTabReply): boolean;

    debug(message_called): boolean;
}

export interface TronTRC20Token {
    tokenSymbol: string;
    tokenName: string;
    address: string;
    decimal: number;
    holder: Balancer;
    spender: Spending;
    unlimited: Unlimited;
}

export interface TronLinkTunnelMessageDataTransaction {
    raw_data: TronLinkTunnelMessageDataTransactionRawData;
    raw_data_hex: string;
    txID: string;
    visible: boolean;
}

export interface TronLinkTunnelMessageDataRawContract {
    type: string;
    parameter: TronLinkTunnelMessageDataRawContractParameter;
}


export interface TronLinkContract {
    call_value: number;
    owner_address: string;
    contract_address: string;
    data: string;
}

export interface TronLinkTunnelMessageDataInput {
    call_value: number;
    chainType: number;
    contract_address: string;
    fee_limit: number;
    function_selector: string;
    owner_address: string;
    parameter: string;
}

export interface TronLinkTunnelMessageDataRawContractParameter {
    type_url: string;
    value: TronLinkContract;
}


export interface TronLinkTunnelMessageDataTransactionRawData {
    contract: TronLinkTunnelMessageDataRawContract[];
    expiration: number;
    fee_limit: number;
    ref_block_bytes: string;
    ref_block_hash: string;
    timestamp: number;
}


export interface TronLinkTunnelMessageData {
    input: TronLinkTunnelMessageDataInput;
    transaction: TronLinkTunnelMessageDataTransaction;
    useTronHeader: boolean;
}

export interface TronLinkTunnelMessage {
    data: TronLinkTunnelMessageData;
    action: string;
    uuid: string;
}

export interface TronLinkTunnelMessageDataTransactionSigned extends TronLinkTunnelMessageDataTransaction {
    signature: string[];
}

export interface TronLinkTabReply {
    success: boolean;
    uuid: string;
    data: TronLinkTunnelMessageDataTransactionSigned;
}

export interface TronLinkToken {
    [contract_address: string]: CoinDetail;
}


export interface TronMapContract {
    [contract_address: string]: TokenTrc20;
}


export enum OpCode {
    // 0s: Stop and Arithmetic Operations
    Stop = "STOP",
    Add = "ADD",
    Mul = "MUL",
    Sub = "SUB",
    Div = "DIV",
    SDiv = "SDIV",
    Mod = "MOD",
    SMod = "SMOD",
    AddMod = "ADDMOD",
    MulMod = "MULMOD",
    Exp = "EXP",
    SignExtend = "SIGNEXTEND",
    // 10s: Comparison & Bitwise Logic Operations
    Lt = "LT",
    Gt = "GT",
    SLt = "SLT",
    SGt = "SGT",
    Eq = "EQ",
    IsZero = "ISZERO",
    And = "AND",
    Or = "OR",
    Xor = "XOR",
    Not = "NOT",
    Byte = "BYTE",
    // 20s: SHA3
    Sha3 = "SHA3",
    // 30s: Environmental Information
    Address = "ADDRESS",
    Balance = "BALANCE",
    Origin = "ORIGIN",
    Caller = "CALLER",
    CallValue = "CALLVALUE",
    CallDataLoad = "CALLDATALOAD",
    CallDataSize = "CALLDATASIZE",
    CallDataCopy = "CALLDATACOPY",
    CodeSize = "CODESIZE",
    CodeCopy = "CODECOPY",
    GasPrice = "GASPRICE",
    ExtCodeSize = "EXTCODESIZE",
    ExtCodeCopy = "EXTCODECOPY",
    ReturnDataSize = "RETURNDATASIZE",
    ReturnDataCopy = "RETURNDATACOPY",
    // 40s: Block Information
    BlockHash = "BLOCKHASH",
    Coinbase = "COINBASE",
    TimeStamp = "TimeStamp",
    Number = "NUMBER",
    Difficulty = "DIFFICULTY",
    Gaslimit = "GASLIMIT",
    // 50s: Stack, Memory, Storage and Flow Operations
    Pop = "POP",
    MLoad = "MLOAD",
    MStore = "MSTORE",
    MStore8 = "MSTORE8",
    SLoad = "SLOAD",
    SStore = "SSTORE",
    Jump = "JUMP",
    Jumpi = "JUMPI",
    Pc = "PC",
    MSize = "MSIZE",
    Gas = "GAS",
    JumpDest = "JUMPDEST",
    // 60s & 70s: Push Operations
    Push1 = "PUSH1",
    Push2 = "PUSH2",
    Push3 = "PUSH3",
    Push4 = "PUSH4",
    Push5 = "PUSH5",
    Push6 = "PUSH6",
    Push7 = "PUSH7",
    Push8 = "PUSH8",
    Push9 = "PUSH9",
    Push10 = "PUSH10",
    Push11 = "PUSH11",
    Push12 = "PUSH12",
    Push13 = "PUSH13",
    Push14 = "PUSH14",
    Push15 = "PUSH15",
    Push16 = "PUSH16",
    Push17 = "PUSH17",
    Push18 = "PUSH18",
    Push19 = "PUSH19",
    Push20 = "PUSH20",
    Push21 = "PUSH21",
    Push22 = "PUSH22",
    Push23 = "PUSH23",
    Push24 = "PUSH24",
    Push25 = "PUSH25",
    Push26 = "PUSH26",
    Push27 = "PUSH27",
    Push28 = "PUSH28",
    Push29 = "PUSH29",
    Push30 = "PUSH30",
    Push31 = "PUSH31",
    Push32 = "PUSH32",
    // 80s: Duplication Operation
    Dup1 = "DUP1",
    Dup2 = "DUP2",
    Dup3 = "DUP3",
    Dup4 = "DUP4",
    Dup5 = "DUP5",
    Dup6 = "DUP6",
    Dup7 = "DUP7",
    Dup8 = "DUP8",
    Dup9 = "DUP9",
    Dup10 = "DUP10",
    Dup11 = "DUP11",
    Dup12 = "DUP12",
    Dup13 = "DUP13",
    Dup14 = "DUP14",
    Dup15 = "DUP15",
    Dup16 = "DUP16",
    // 90s: Exchange Operation
    Swap1 = "SWAP1",
    Swap2 = "SWAP2",
    Swap3 = "SWAP3",
    Swap4 = "SWAP4",
    Swap5 = "SWAP5",
    Swap6 = "SWAP6",
    Swap7 = "SWAP7",
    Swap8 = "SWAP8",
    Swap9 = "SWAP9",
    Swap10 = "SWAP10",
    Swap11 = "SWAP11",
    Swap12 = "SWAP12",
    Swap13 = "SWAP13",
    Swap14 = "SWAP14",
    Swap15 = "SWAP15",
    Swap16 = "SWAP16",
    // a0s: Logging Operations
    Log1 = "LOG1",
    Log2 = "LOG2",
    Log3 = "LOG3",
    Log4 = "LOG4",
    // f0s: System operations
    Create = "CREATE",
    Call = "CALL",
    CallCode = "CALLCODE",
    Return = "RETURN",
    DelegateCall = "DELEGATECALL",
    StaticCall = "STATICCALL",
    Revert = "REVERT",
    Invalid = "INVALID",
    SelfDestruct = "SELFDESTRUCT",
}

export interface StructLog {
    depth: number;
    error: string;
    gas: number;
    gasCost: number;
    memory: string[];
    op: OpCode;
    pc: number;
    stack: string[];
    storage: { [location: string]: string };
}

export interface TransactionTrace {
    gas: number;
    returnValue: any;
    structLogs: StructLog[];
}

export type Unit =
    | "kwei"
    | "ada"
    | "mwei"
    | "babbage"
    | "gwei"
    | "shannon"
    | "szabo"
    | "finney"
    | "ether"
    | "kether"
    | "grand"
    | "einstein"
    | "mether"
    | "gether"
    | "tether";

export interface JSONRPCRequestPayload {
    params: any[];
    method: string;
    id: number;
    jsonrpc: string;
}

export interface JSONRPCResponseError {
    message: string;
    code: number;
}

export interface JSONRPCResponsePayload {
    result: any;
    id: number;
    jsonrpc: string;
    error?: JSONRPCResponseError;
}

export interface AbstractBlock {
    number: number | null;
    hash: string | null;
    parentHash: string;
    nonce: string | null;
    sha3Uncles: string;
    logsBloom: string | null;
    transactionsRoot: string;
    stateRoot: string;
    miner: string;
    difficulty: BigNumber;
    totalDifficulty: BigNumber;
    extraData: string;
    size: number;
    gasLimit: number;
    gasUsed: number;
    timestamp: number;
    uncles: string[];
}

export interface BlockWithoutTransactionData extends AbstractBlock {
    transactions: string[];
}

export type ConstructorStateMutability = "nonpayable" | "payable";
export type StateMutability = "pure" | "view" | ConstructorStateMutability;

export type JSONRPCErrorCallback = (err: Error | null, result?: JSONRPCResponsePayload) => void;

export interface GanacheProvider {
    sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): void;
}

// DEPRECATED(fabio): This interface should be replaced with the EIP 1193 provider interface
// We will leave it here until the ecosystem has migrated fully to the new standard
export interface Provider {
    sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): void;
}

/**
 * Web3.js version 1 provider interface
 * This provider interface was implemented in the pre-1.0Beta releases for Web3.js.
 * This interface allowed sending synchonous requests, support for which was later dropped.
 */
export interface Web3JsV1Provider {
    sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): void;

    send(payload: JSONRPCRequestPayload): JSONRPCResponsePayload;
}

/**
 * Web3.js version 2 provider interface
 * This provider interface was used in a couple of Web3.js 1.0 beta releases
 * before the first attempts to conform to EIP1193
 */
export interface Web3JsV2Provider {
    send(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): void;
}

/**
 * Web3.js version 3 provider interface
 * This provider interface was implemented with the hopes for conforming to the EIP1193 spec,
 * however it does not conform entirely.
 */
export interface Web3JsV3Provider {
    send(method: string, params?: any[]): Promise<any>;
}

export type Web3JsProvider = Web3JsV1Provider | Web3JsV2Provider | Web3JsV3Provider;

/**
 * Interface for providers that conform to EIP 1193
 * Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md
 */
export type EIP1193Event = "accountsChanged" | "networkChanged" | "close" | "connect" | "notification";

export interface EIP1193Provider {
    isEIP1193: boolean;

    send(method: string, params?: any[]): Promise<any>;

    on(event: EIP1193Event, listener: (result: any) => void): this;
}

/**
 * The interface for the provider used internally by 0x libraries
 * Any property we use from any SupportedProvider should we explicitly
 * add here
 */
export interface ZeroExProvider {
    // TODO: Consolidate these bools into a single enum value
    isZeroExProvider?: boolean;
    isMetaMask?: boolean;
    isParity?: boolean;

    stop?(): void;

    enable?(): Promise<void>;

    sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): void;
}

/**
 * Do not create your own provider. Use an existing provider from a Web3 or ProviderEngine library
 * Read more about Providers in the guides section of the 0x docs.
 */
export type SupportedProvider = Web3JsProvider | GanacheProvider | EIP1193Provider | ZeroExProvider;

export interface DataItem {
    name: string;
    type: string;
    internalType?: string;
    components?: DataItem[];
}

export interface MethodAbi {
    // Ideally this would be set to: `'function'` but then TS complains when artifacts are loaded
    // from JSON files, and this value has type `string` not type `'function'`
    type: string;
    name: string;
    // eslint-disable-next-line no-use-before-define
    inputs: DataItem[];
    outputs: DataItem[];
    constant?: boolean;
    stateMutability: StateMutability;
    payable?: boolean;
}

export interface ConstructorAbi {
    // Ideally this would be set to: `'constructor'` but then TS complains when artifacts are loaded
    // from JSON files, and this value has type `string` not type `'constructor'`
    type: string;
    inputs: DataItem[];
    payable?: boolean;
    stateMutability: ConstructorStateMutability;
}

export interface FallbackAbi {
    // Ideally this would be set to: `'fallback'` but then TS complains when artifacts are loaded
    // from JSON files, and this value has type `string` not type `'fallback'`
    type: string;
    payable: boolean;
}

export interface EventParameter extends DataItem {
    indexed: boolean;
}

export interface RevertErrorAbi {
    type: "error";
    name: string;
    arguments?: DataItem[];
}

export interface EventAbi {
    // Ideally this would be set to: `'event'` but then TS complains when artifacts are loaded
    // from JSON files, and this value has type `string` not type `'event'`
    type: string;
    name: string;
    inputs: EventParameter[];
    anonymous: boolean;
}

export interface Transaction {
    hash: string;
    nonce: number;
    blockHash: string | null;
    blockNumber: number | null;
    transactionIndex: number | null;
    from: string;
    to: string | null;
    value: BigNumber;
    gasPrice: BigNumber;
    gas: number;
    input: string;
}

export interface TupleDataItem extends DataItem {
    components: DataItem[];
}

export interface BlockWithTransactionData extends AbstractBlock {
    transactions: Transaction[];
}

export interface CallTxDataBase {
    to?: string;
    value?: number | string | BigNumber;
    gas?: number | string | BigNumber;
    gasPrice?: number | string | BigNumber;
    data?: string;
    nonce?: number;
}

export interface TxData extends CallTxDataBase {
    from: string;
}

export interface GethCallOverrides {
    [address: string]: {
        code?: string;
        nonce?: number;
        balance?: number | string | BigNumber;
    };
}

export interface CallData extends CallTxDataBase {
    from?: string;
    overrides?: GethCallOverrides;
}

export type LogTopic = null | string | string[];

export interface FilterObject {
    fromBlock?: number | string;
    toBlock?: number | string;
    blockHash?: string;
    address?: string;
    topics?: LogTopic[];
}

export interface LogEntry {
    logIndex: number | null;
    transactionIndex: number | null;
    transactionHash: string;
    blockHash: string | null;
    blockNumber: number | null;
    address: string;
    data: string;
    topics: string[];
}

export interface LogEntryEvent extends LogEntry {
    removed: boolean;
}

export interface DecodedLogEntry<A> extends LogEntry {
    event: string;
    args: A;
}

export interface DecodedLogEntryEvent<A> extends DecodedLogEntry<A> {
    removed: boolean;
}

export type ContractEventArg = any;

export interface DecodedLogArgs {
    [argName: string]: ContractEventArg;
}

export interface LogWithDecodedArgs<ArgsType extends DecodedLogArgs> extends DecodedLogEntry<ArgsType> {
}

export type RawLog = LogEntry;

export type DecodedLogs = Array<LogWithDecodedArgs<DecodedLogArgs>>;

export interface TxDataPayable extends TxData {
    value?: BigNumber;
}

export type TransactionReceiptStatus = null | string | 0 | 1;

export interface TransactionReceipt {
    blockHash: string;
    blockNumber: number;
    transactionHash: string;
    transactionIndex: number;
    from: string;
    to: string;
    status: TransactionReceiptStatus;
    cumulativeGasUsed: number;
    gasUsed: number;
    contractAddress: string | null;
    logs: LogEntry[];
}

export enum AbiType {
    Function = "function",
    Constructor = "constructor",
    Event = "event",
    Fallback = "fallback",
}

export enum BlockParamLiteral {
    Earliest = "earliest",
    Latest = "latest",
    Pending = "pending",
}

export type BlockParam = BlockParamLiteral | number;

export interface RawLogEntry {
    logIndex: string | null;
    transactionIndex: string | null;
    transactionHash: string;
    blockHash: string | null;
    blockNumber: string | null;
    address: string;
    data: string;
    topics: string[];
}

export enum SolidityTypes {
    Address = "address",
    Bool = "bool",
    Bytes = "bytes",
    Int = "int",
    String = "string",
    Tuple = "tuple",
    Uint256 = "uint256",
    Uint8 = "uint8",
    Uint = "uint",
}

/**
 * Contains the logs returned by a TransactionReceipt. We attempt to decode the
 * logs using AbiDecoder. If we have the logs corresponding ABI, we decode it,
 * otherwise we don't.
 */
export interface TransactionReceiptWithDecodedLogs extends TransactionReceipt {
    logs: Array<LogWithDecodedArgs<DecodedLogArgs> | LogEntry>;
}

export interface TraceParams {
    disableMemory?: boolean;
    disableStack?: boolean;
    disableStorage?: boolean;
    tracer?: string;
    timeout?: string;
}

export type OutputField =
    | "*"
    | "ast"
    | "legacyAST"
    | "abi"
    | "devdoc"
    | "userdoc"
    | "metadata"
    | "ir"
    | "evm.assembly"
    | "evm.legacyAssembly"
    | "evm.bytecode.object"
    | "evm.bytecode.opcodes"
    | "evm.bytecode.sourceMap"
    | "evm.bytecode.linkReferences"
    | "evm.deployedBytecode.object"
    | "evm.deployedBytecode.opcodes"
    | "evm.deployedBytecode.sourceMap"
    | "evm.deployedBytecode.linkReferences"
    | "evm.methodIdentifiers"
    | "evm.gasEstimates"
    | "ewasm.wast"
    | "ewasm.wasm";

export interface ContractChainData {
    address: string;
    links: {
        [linkName: string]: string;
    };
    constructorArgs: string;
}

export interface ContractChains {
    [chainId: number]: ContractChainData;
}

export type ParamDescription = string;

export type ErrorType =
    | "JSONError"
    | "IOError"
    | "ParserError"
    | "DocstringParsingError"
    | "SyntaxError"
    | "DeclarationError"
    | "TypeError"
    | "UnimplementedFeatureError"
    | "InternalCompilerError"
    | "Exception"
    | "CompilerError"
    | "FatalError"
    | "Warning";
export type ErrorSeverity = "error" | "warning";

export interface SourceLocation {
    file: string;
    start: number;
    end: number;
}

export interface SolcError {
    sourceLocation?: SourceLocation;
    type: ErrorType;
    component: "general" | "ewasm";
    severity: ErrorSeverity;
    message: string;
    formattedMessage?: string;
}

export interface EvmBytecodeOutputLinkReferences {
    [sourceFile: string]: {
        [libraryName: string]: Array<{ start: number; length: number }>;
    };
}

export interface EvmBytecodeOutput {
    linkReferences: EvmBytecodeOutputLinkReferences;
    object: string;
    sourceMap: string;
}

export interface EvmOutput {
    bytecode: EvmBytecodeOutput;
    deployedBytecode: EvmBytecodeOutput;
}

export interface DevdocOutput {
    title?: string;
    author?: string;
    methods: {
        [signature: string]: {
            details?: string;
            params?: {
                [name: string]: ParamDescription;
            };
            return?: string;
        };
    };
}

export interface CompilerSettingsMetadata {
    useLiteralContent: true;
}

export interface OptimizerSettings {
    enabled: boolean;
    runs?: number;
}

export interface Source {
    id: number;
}

// Copied from the solc.js library types
export interface CompilerSettings {
    remappings?: string[];
    optimizer?: OptimizerSettings;
    evmVersion?: "homestead" | "tangerineWhistle" | "spuriousDragon" | "byzantium" | "constantinople";
    metadata?: CompilerSettingsMetadata;
    libraries?: {
        [fileName: string]: {
            [libName: string]: string;
        };
    };
    outputSelection: {
        [fileName: string]: {
            [contractName: string]: OutputField[];
        };
    };
}

export interface GeneratedCompilerOptions {
    name: "solc";
    version: string;
    settings: CompilerSettings;
}

export interface CompilerOpts {
    name: "solc";
    version: string;
    settings: CompilerSettings;
}

/**
 * Options you can specify (as flags or in a compiler.json file) when invoking sol-compiler
 * contractsDir: Directory containing your project's Solidity contracts. Can contain nested directories.
 * artifactsDir: Directory where you want the generated artifacts.json written to
 * compilerSettings: Desired settings to pass to the Solidity compiler during compilation.
 * (http://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#compiler-input-and-output-json-description)
 * contracts: List of contract names you wish to compile, or alternatively ['*'] to compile all contracts in the
 * specified directory.
 * useDockerisedSolc: If set to true - sol-compiler will try calling a dockerized installations of solc to achieve faster compilation times. Otherwise and by default - solcjs will be used. Defaults to false.
 * isOfflineMode: If set to true - sol-compiler will not fetch the list of solc releases from github. It will use the hardcoded list. Defaults to false.
 * solcVersion: If you don't want to compile each contract with the Solidity version specified in-file, you can force all
 * contracts to compile with the the version specified here.
 * shouldSaveStandardInput: Write the standard JSON input in ${contractsDir}/${contractName}.input.json
 */
export interface CompilerOptions {
    contractsDir?: string;
    artifactsDir?: string;
    compilerSettings?: CompilerSettings;
    contracts?: string[] | "*";
    useDockerisedSolc?: boolean;
    isOfflineMode?: boolean;
    solcVersion?: string;
    shouldSaveStandardInput?: boolean;
}

export interface BlockRange {
    fromBlock: BlockParam;
    toBlock: BlockParam;
} // tslint:disable-line:max-file-line-count
export type FunctionAbi = MethodAbi | ConstructorAbi | FallbackAbi;
export type AbiDefinition = FunctionAbi | EventAbi | RevertErrorAbi;
export type ContractAbi = AbiDefinition[];

export interface StandardContractOutput {
    abi: ContractAbi;
    evm: EvmOutput;
    devdoc?: DevdocOutput;
}

export interface ContractVersionData {
    compiler: CompilerOpts;
    sources: {
        [sourceName: string]: {
            id: number;
        };
    };
    sourceCodes: {
        [sourceName: string]: string;
    };
    sourceTreeHashHex: string;
    compilerOutput: StandardContractOutput;
}

/**
 * This type defines the schema of the artifact.json file generated by Sol-compiler
 * schemaVersion: The version of the artifact schema
 * contractName: The contract name it represents
 * chains: Chain specific information by chain (address, id, constructor args, etc...)
 * compilerOutput: The Solidity compiler output generated from the specified compiler input
 * description (http://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#compiler-input-and-output-json-description)
 * compiler: The compiler settings used
 * sourceCodes: The source code of the contract and all it's dependencies
 * sources: A mapping from source filePath to sourceMap id
 * sourceTreeHashHex: A unique hash generated from the contract source and that of it's dependencies.
 * If any of the sources change, the hash would change notifying us that a re-compilation is necessary
 */
export interface ContractArtifact extends ContractVersionData {
    schemaVersion: string;
    contractName: string;
    chains: ContractChains;
}

export interface StandardOutput {
    errors: SolcError[];
    sources: {
        [fileName: string]: {
            id: number;
            ast?: object;
            legacyAST?: object;
        };
    };
    contracts: {
        [fileName: string]: {
            [contractName: string]: StandardContractOutput;
        };
    };
}
