import '../common/eager_offset';
import { Bytes } from '../common/collections';

export namespace cosmos {
  export class Block {
    constructor(
      public header: Header,
      public evidence: EvidenceList,
      public lastCommit: Commit,
      public resultBeginBlock: ResponseBeginBlock,
      public resultEndBlock: ResponseEndBlock,
      public transactions: Array<TxResult>,
      public validatorUpdates: Array<Validator>,
    ) {}
  }

  export class HeaderOnlyBlock {
    constructor(public header: Header) {}
  }

  export class EventData {
    constructor(
      public event: Event,
      public block: HeaderOnlyBlock,
      public tx: TransactionContext,
    ) {}
  }

  export class TransactionData {
    constructor(public tx: TxResult, public block: HeaderOnlyBlock) {}
  }

  export class MessageData {
    constructor(
      public message: Any,
      public block: HeaderOnlyBlock,
      public tx: TransactionContext,
    ) {}
  }

  export class TransactionContext {
    constructor(
      public hash: Bytes,
      public index: u32,
      public code: u32,
      public gasWanted: i64,
      public gasUsed: i64,
    ) {}
  }

  export class Header {
    constructor(
      public version: Consensus,
      public chainId: string,
      public height: u64,
      public time: Timestamp,
      public lastBlockId: BlockID,
      public lastCommitHash: Bytes,
      public dataHash: Bytes,
      public validatorsHash: Bytes,
      public nextValidatorsHash: Bytes,
      public consensusHash: Bytes,
      public appHash: Bytes,
      public lastResultsHash: Bytes,
      public evidenceHash: Bytes,
      public proposerAddress: Bytes,
      public hash: Bytes,
    ) {}
  }

  export class Consensus {
    constructor(public block: u64, public app: u64) {}
  }

  export class Timestamp {
    constructor(public seconds: i64, public nanos: i32) {}
  }

  export class BlockID {
    constructor(public hash: Bytes, public partSetHeader: PartSetHeader) {}
  }

  export class PartSetHeader {
    constructor(public total: u32, public hash: Bytes) {}
  }

  export class EvidenceList {
    constructor(public evidence: Array<Evidence>) {}
  }

  export class Evidence {
    constructor(
      public duplicateVoteEvidence: DuplicateVoteEvidence,
      public lightClientAttackEvidence: LightClientAttackEvidence,
    ) {}
  }

  export class DuplicateVoteEvidence {
    constructor(
      public voteA: EventVote,
      public voteB: EventVote,
      public totalVotingPower: i64,
      public validatorPower: i64,
      public timestamp: Timestamp,
    ) {}
  }

  export class EventVote {
    constructor(
      public eventVoteType: SignedMsgType,
      public height: u64,
      public round: i32,
      public blockId: BlockID,
      public timestamp: Timestamp,
      public validatorAddress: Bytes,
      public validatorIndex: i32,
      public signature: Bytes,
    ) {}
  }

  export enum SignedMsgType {
    SIGNED_MSG_TYPE_UNKNOWN = 0,
    SIGNED_MSG_TYPE_PREVOTE = 1,
    SIGNED_MSG_TYPE_PRECOMMIT = 2,
    SIGNED_MSG_TYPE_PROPOSAL = 32,
  }

  export class LightClientAttackEvidence {
    constructor(
      public conflictingBlock: LightBlock,
      public commonHeight: i64,
      public byzantineValidators: Array<Validator>,
      public totalVotingPower: i64,
      public timestamp: Timestamp,
    ) {}
  }

  export class LightBlock {
    constructor(public signedHeader: SignedHeader, public validatorSet: ValidatorSet) {}
  }

  export class SignedHeader {
    constructor(public header: Header, public commit: Commit) {}
  }

  export class Commit {
    constructor(
      public height: i64,
      public round: i32,
      public blockId: BlockID,
      public signatures: Array<CommitSig>,
    ) {}
  }

  export class CommitSig {
    constructor(
      public blockIdFlag: BlockIDFlag,
      public validatorAddress: Bytes,
      public timestamp: Timestamp,
      public signature: Bytes,
    ) {}
  }

  export enum BlockIDFlag {
    BLOCK_ID_FLAG_UNKNOWN = 0,
    BLOCK_ID_FLAG_ABSENT = 1,
    BLOCK_ID_FLAG_COMMIT = 2,
    BLOCK_ID_FLAG_NIL = 3,
  }

  export class ValidatorSet {
    constructor(
      public validators: Array<Validator>,
      public proposer: Validator,
      public totalVotingPower: i64,
    ) {}
  }

  export class Validator {
    constructor(
      public address: Bytes,
      public pubKey: PublicKey,
      public votingPower: i64,
      public proposerPriority: i64,
    ) {}
  }

  export class PublicKey {
    constructor(public ed25519: Bytes, public secp256k1: Bytes) {}
  }

  export class ResponseBeginBlock {
    constructor(public events: Array<Event>) {}
  }

  export class Event {
    constructor(public eventType: string, public attributes: Array<EventAttribute>) {}

    getAttribute(key: string): EventAttribute | null {
      for (let i = 0; i < this.attributes.length; i++) {
        if (this.attributes[i].key == key) {
          return this.attributes[i];
        }
      }
      return null;
    }

    getAttributeValue(key: string): string {
      const attribute = this.getAttribute(key);
      return attribute ? attribute.value : '';
    }
  }

  export class EventAttribute {
    constructor(public key: string, public value: string, public index: bool) {}
  }

  export class ResponseEndBlock {
    constructor(
      public validatorUpdates: Array<ValidatorUpdate>,
      public consensusParamUpdates: ConsensusParams,
      public events: Array<Event>,
    ) {}
  }

  export class ValidatorUpdate {
    constructor(public address: Bytes, public pubKey: PublicKey, public power: i64) {}
  }

  export class ConsensusParams {
    constructor(
      public block: BlockParams,
      public evidence: EvidenceParams,
      public validator: ValidatorParams,
      public version: VersionParams,
    ) {}
  }

  export class BlockParams {
    constructor(public maxBytes: i64, public maxGas: i64) {}
  }

  export class EvidenceParams {
    constructor(
      public maxAgeNumBlocks: i64,
      public maxAgeDuration: Duration,
      public maxBytes: i64,
    ) {}
  }

  export class Duration {
    constructor(public seconds: i64, public nanos: i32) {}
  }

  export class ValidatorParams {
    constructor(public pubKeyTypes: Array<string>) {}
  }

  export class VersionParams {
    constructor(public appVersion: u64) {}
  }

  export class TxResult {
    constructor(
      public height: u64,
      public index: u32,
      public tx: Tx,
      public result: ResponseDeliverTx,
      public hash: Bytes,
    ) {}
  }

  export class Tx {
    constructor(public body: TxBody, public authInfo: AuthInfo, public signatures: Array<Bytes>) {}
  }

  export class TxBody {
    constructor(
      public messages: Array<Any>,
      public memo: string,
      public timeoutHeight: u64,
      public extensionOptions: Array<Any>,
      public nonCriticalExtensionOptions: Array<Any>,
    ) {}
  }

  export class Any {
    constructor(public typeUrl: string, public value: Bytes) {}
  }

  export class AuthInfo {
    constructor(public signerInfos: Array<SignerInfo>, public fee: Fee, public tip: Tip) {}
  }

  export class SignerInfo {
    constructor(public publicKey: Any, public modeInfo: ModeInfo, public sequence: u64) {}
  }

  export class ModeInfo {
    constructor(public single: ModeInfoSingle, public multi: ModeInfoMulti) {}
  }

  export class ModeInfoSingle {
    constructor(public mode: SignMode) {}
  }

  export enum SignMode {
    SIGN_MODE_UNSPECIFIED = 0,
    SIGN_MODE_DIRECT = 1,
    SIGN_MODE_TEXTUAL = 2,
    SIGN_MODE_LEGACY_AMINO_JSON = 127,
  }

  export class ModeInfoMulti {
    constructor(public bitarray: CompactBitArray, public modeInfos: Array<ModeInfo>) {}
  }

  export class CompactBitArray {
    constructor(public extraBitsStored: u32, public elems: Bytes) {}
  }

  export class Fee {
    constructor(
      public amount: Array<Coin>,
      public gasLimit: u64,
      public payer: string,
      public granter: string,
    ) {}
  }

  export class Coin {
    constructor(public denom: string, public amount: string) {}
  }

  export class Tip {
    constructor(public amount: Array<Coin>, public tipper: string) {}
  }

  export class ResponseDeliverTx {
    constructor(
      public code: u32,
      public data: Bytes,
      public log: string,
      public info: string,
      public gasWanted: i64,
      public gasUsed: i64,
      public events: Array<Event>,
      public codespace: string,
    ) {}
  }

  export class ValidatorSetUpdates {
    constructor(public validatorUpdates: Array<Validator>) {}
  }
}
