import { GenericDocument, Field } from './GenericDocument';

/// All possible root document types.
export type DocumentsModelRootType =
  | typeof MRZDocumentType
  | typeof DeIdCardFrontDocumentType
  | typeof DeIdCardBackDocumentType
  | typeof DePassportDocumentType
  | typeof DeDriverLicenseFrontDocumentType
  | typeof DeDriverLicenseBackDocumentType
  | typeof DeResidencePermitFrontDocumentType
  | typeof DeResidencePermitBackDocumentType
  | typeof EuropeanHealthInsuranceCardDocumentType
  | typeof DeHealthInsuranceCardFrontDocumentType;

export const MRZDocumentType = 'MRZ';
export const DeIdCardFrontDocumentType = 'DeIdCardFront';
export const DeIdCardBackDocumentType = 'DeIdCardBack';
export const DePassportDocumentType = 'DePassport';
export const DeDriverLicenseFrontDocumentType = 'DeDriverLicenseFront';
export const DeDriverLicenseBackDocumentType = 'DeDriverLicenseBack';
export const DeDriverLicenseBackCategoryDocumentType = 'Category';
export const DeDriverLicenseBackCategoriesDocumentType = 'Categories';
export const DeDriverLicenseBackCategoriesADocumentType = 'A';
export const DeDriverLicenseBackCategoriesA1DocumentType = 'A1';
export const DeDriverLicenseBackCategoriesA2DocumentType = 'A2';
export const DeDriverLicenseBackCategoriesAMDocumentType = 'AM';
export const DeDriverLicenseBackCategoriesBDocumentType = 'B';
export const DeDriverLicenseBackCategoriesB1DocumentType = 'B1';
export const DeDriverLicenseBackCategoriesBEDocumentType = 'BE';
export const DeDriverLicenseBackCategoriesCDocumentType = 'C';
export const DeDriverLicenseBackCategoriesC1DocumentType = 'C1';
export const DeDriverLicenseBackCategoriesC1EDocumentType = 'C1E';
export const DeDriverLicenseBackCategoriesCEDocumentType = 'CE';
export const DeDriverLicenseBackCategoriesDDocumentType = 'D';
export const DeDriverLicenseBackCategoriesD1DocumentType = 'D1';
export const DeDriverLicenseBackCategoriesD1EDocumentType = 'D1E';
export const DeDriverLicenseBackCategoriesDEDocumentType = 'DE';
export const DeDriverLicenseBackCategoriesLDocumentType = 'L';
export const DeDriverLicenseBackCategoriesTDocumentType = 'T';
export const DeResidencePermitFrontDocumentType = 'DeResidencePermitFront';
export const DeResidencePermitBackDocumentType = 'DeResidencePermitBack';
export const EuropeanHealthInsuranceCardDocumentType = 'EuropeanHealthInsuranceCard';
export const DeHealthInsuranceCardFrontDocumentType = 'DeHealthInsuranceCardFront';

/** MRZ part of the document */
export class MRZ {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return MRZDocumentType;
  }

  /** Birth date */
  get birthDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate') as Field;
  }

  /** Check digit birth date */
  get checkDigitBirthDate(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CheckDigitBirthDate');
  }

  /** Check digit document number */
  get checkDigitDocumentNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CheckDigitDocumentNumber');
  }

  /** Check digit expiry date */
  get checkDigitExpiryDate(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CheckDigitExpiryDate');
  }

  /** Check  digit general */
  get checkDigitGeneral(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CheckDigitGeneral');
  }

  /** Check digit personal number */
  get checkDigitPersonalNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CheckDigitPersonalNumber');
  }

  /** Date of issuance */
  get dateOfIssuance(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'DateOfIssuance');
  }

  /** Document number */
  get documentNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'DocumentNumber');
  }

  /** Document type code */
  get documentTypeCode(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'DocumentTypeCode');
  }

  /** Expiry date */
  get expiryDate(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate');
  }

  /** Gender */
  get gender(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Gender');
  }

  /** Given names */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Issuing authority */
  get issuingAuthority(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuingAuthority');
  }

  /** Language code */
  get languageCode(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'LanguageCode');
  }

  /** Nationality */
  get nationality(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Nationality') as Field;
  }

  /** Office of issuance */
  get officeOfIssuance(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'OfficeOfIssuance');
  }

  /** TD1 Optional field (line 2) */
  get optional1(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Optional1');
  }

  /** TD1 Optional field (line 3) */
  get optional2(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Optional2');
  }

  /** PIN code */
  get pinCode(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'PINCode');
  }

  /** Personal number */
  get personalNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'PersonalNumber');
  }

  /** Surname */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }

  /** Travel document type */
  get travelDocType(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'TravelDocType');
  }

  /** Travel document type variant */
  get travelDocTypeVariant(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'TravelDocTypeVariant');
  }

  /** Unknown */
  get unknown(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Unknown');
  }

  /** Version number */
  get versionNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'VersionNumber');
  }

  /** MRV-A/MRV-B (Travel Visa) Optional field */
  get visaOptional(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'VisaOptional');
  }
}

/** German ID card, front side */
export class DeIdCardFront {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeIdCardFrontDocumentType;
  }

  /** Birth date */
  get birthDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate') as Field;
  }

  /** Birthplace */
  get birthplace(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Birthplace') as Field;
  }

  /** Six digit card access number */
  get cardAccessNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'CardAccessNumber') as Field;
  }

  /** Expiry date */
  get expiryDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate') as Field;
  }

  /** Given names */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Document ID number (in the top-right corner) */
  get id(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ID') as Field;
  }

  /** Maiden name */
  get maidenName(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'MaidenName');
  }

  /** Nationality */
  get nationality(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Nationality') as Field;
  }

  /** Photo image */
  get photo(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Photo') as Field;
  }

  /** Signature image */
  get signature(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Signature') as Field;
  }

  /** Surname */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }
}

/** German ID card, back side */
export class DeIdCardBack {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeIdCardBackDocumentType;
  }

  /** Address */
  get address(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Address') as Field;
  }

  /** Eye color */
  get eyeColor(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'EyeColor') as Field;
  }

  /** Height */
  get height(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Height') as Field;
  }

  /** Issue date */
  get issueDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssueDate') as Field;
  }

  /** Issuing authority */
  get issuingAuthority(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuingAuthority') as Field;
  }

  /** Pseudonym */
  get pseudonym(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Pseudonym');
  }

  /** Raw MRZ text value */
  get rawMRZ(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RawMRZ') as Field;
  }

  /** The child document of type "MRZ". */
  get mrz(): MRZ {
    const child = this.document.children.find((c: GenericDocument) => c.type.name === 'MRZ');
    return new MRZ(child as GenericDocument);
  }
}

export namespace DeIdCardBack {}

/** German travel passport (Reisepass) */
export class DePassport {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DePassportDocumentType;
  }

  /** Birth date */
  get birthDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate') as Field;
  }

  /** Birthplace */
  get birthplace(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Birthplace') as Field;
  }

  /** Country code */
  get countryCode(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'CountryCode') as Field;
  }

  /** Expiry date */
  get expiryDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate') as Field;
  }

  /** Gender */
  get gender(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Gender') as Field;
  }

  /** Given names */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Document ID number (in the top-right corner) */
  get id(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ID') as Field;
  }

  /** Issue date */
  get issueDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssueDate') as Field;
  }

  /** Issuing authority */
  get issuingAuthority(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuingAuthority') as Field;
  }

  /** Maiden name */
  get maidenName(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'MaidenName');
  }

  /** Nationality */
  get nationality(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Nationality') as Field;
  }

  /** Passport type */
  get passportType(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'PassportType') as Field;
  }

  /** Photo image */
  get photo(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Photo') as Field;
  }

  /** Raw MRZ text value */
  get rawMRZ(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RawMRZ') as Field;
  }

  /** Signature image */
  get signature(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Signature') as Field;
  }

  /** Surname */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }

  /** The child document of type "MRZ". */
  get mrz(): MRZ {
    const child = this.document.children.find((c: GenericDocument) => c.type.name === 'MRZ');
    return new MRZ(child as GenericDocument);
  }
}

export namespace DePassport {}

/** German driver license (Führerschein), front side */
export class DeDriverLicenseFront {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeDriverLicenseFrontDocumentType;
  }

  /** Birth date (Field 3.) */
  get birthDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate') as Field;
  }

  /** Birthplace (Field 3.) */
  get birthplace(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Birthplace') as Field;
  }

  /** Expiry date (Field 4b.) */
  get expiryDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate') as Field;
  }

  /** Given names (Field 2.) */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Document ID number (in the top-right corner, Field 5.) */
  get id(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ID') as Field;
  }

  /** Issue date (Field 4a.) */
  get issueDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssueDate') as Field;
  }

  /** Issuing authority (Field 4c.) */
  get issuingAuthority(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuingAuthority') as Field;
  }

  /** Driver's license categories (Field 9.) */
  get licenseCategories(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'LicenseCategories') as Field;
  }

  /** Photo image */
  get photo(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Photo') as Field;
  }

  /** Signature image (Field 7.) */
  get signature(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Signature') as Field;
  }

  /** Surname (Field 1.) */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }
}

/** German driver license (Führerschein), back side */
export class DeDriverLicenseBack {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeDriverLicenseBackDocumentType;
  }

  /** Restrictions applied for the driver's license (Field 12.) */
  get restrictions(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Restrictions');
  }

  /** The child document of type "Categories". */
  get categories(): DeDriverLicenseBack.Categories {
    const child = this.document.children.find((c: GenericDocument) => c.type.name === 'Categories');
    return new DeDriverLicenseBack.Categories(child as GenericDocument);
  }
}

export namespace DeDriverLicenseBack {
  /** A category row from the categories table */
  export abstract class Category {
    private _document: GenericDocument;

    get document(): GenericDocument {
      return this._document;
    }

    constructor(document: GenericDocument) {
      if (document.type.name !== this.requiredDocumentType()) {
        throw new Error(
          `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
        );
      }

      this._document = document;
    }

    abstract requiredDocumentType(): string;

    /** Restrictions (Column 12.) */
    get restrictions(): Field | undefined {
      return this.document.fields.find((f: Field) => f.type.name === 'Restrictions');
    }

    /** Valid from (Column 10.) */
    get validFrom(): Field {
      return this.document.fields.find((f: Field) => f.type.name === 'ValidFrom') as Field;
    }

    /** Valid until (Column 11.) */
    get validUntil(): Field {
      return this.document.fields.find((f: Field) => f.type.name === 'ValidUntil') as Field;
    }
  }

  /** Categories table row container */
  export class Categories {
    private _document: GenericDocument;

    get document(): GenericDocument {
      return this._document;
    }

    constructor(document: GenericDocument) {
      if (document.type.name !== this.requiredDocumentType()) {
        throw new Error(
          `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
        );
      }

      this._document = document;
    }

    requiredDocumentType(): string {
      return DeDriverLicenseBackCategoriesDocumentType;
    }

    /** The child document of type "A". */
    get a(): DeDriverLicenseBack.Categories.A {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'A');
      return new DeDriverLicenseBack.Categories.A(child as GenericDocument);
    }

    /** The child document of type "A1". */
    get a1(): DeDriverLicenseBack.Categories.A1 {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'A1');
      return new DeDriverLicenseBack.Categories.A1(child as GenericDocument);
    }

    /** The child document of type "A2". */
    get a2(): DeDriverLicenseBack.Categories.A2 {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'A2');
      return new DeDriverLicenseBack.Categories.A2(child as GenericDocument);
    }

    /** The child document of type "AM". */
    get am(): DeDriverLicenseBack.Categories.AM {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'AM');
      return new DeDriverLicenseBack.Categories.AM(child as GenericDocument);
    }

    /** The child document of type "B". */
    get b(): DeDriverLicenseBack.Categories.B {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'B');
      return new DeDriverLicenseBack.Categories.B(child as GenericDocument);
    }

    /** The child document of type "B1". */
    get b1(): DeDriverLicenseBack.Categories.B1 | undefined {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'B1');
      return child ? new DeDriverLicenseBack.Categories.B1(child) : undefined;
    }

    /** The child document of type "BE". */
    get be(): DeDriverLicenseBack.Categories.BE {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'BE');
      return new DeDriverLicenseBack.Categories.BE(child as GenericDocument);
    }

    /** The child document of type "C". */
    get c(): DeDriverLicenseBack.Categories.C {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'C');
      return new DeDriverLicenseBack.Categories.C(child as GenericDocument);
    }

    /** The child document of type "C1". */
    get c1(): DeDriverLicenseBack.Categories.C1 {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'C1');
      return new DeDriverLicenseBack.Categories.C1(child as GenericDocument);
    }

    /** The child document of type "C1E". */
    get c1E(): DeDriverLicenseBack.Categories.C1E {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'C1E');
      return new DeDriverLicenseBack.Categories.C1E(child as GenericDocument);
    }

    /** The child document of type "CE". */
    get ce(): DeDriverLicenseBack.Categories.CE {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'CE');
      return new DeDriverLicenseBack.Categories.CE(child as GenericDocument);
    }

    /** The child document of type "D". */
    get d(): DeDriverLicenseBack.Categories.D {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'D');
      return new DeDriverLicenseBack.Categories.D(child as GenericDocument);
    }

    /** The child document of type "D1". */
    get d1(): DeDriverLicenseBack.Categories.D1 {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'D1');
      return new DeDriverLicenseBack.Categories.D1(child as GenericDocument);
    }

    /** The child document of type "D1E". */
    get d1E(): DeDriverLicenseBack.Categories.D1E {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'D1E');
      return new DeDriverLicenseBack.Categories.D1E(child as GenericDocument);
    }

    /** The child document of type "DE". */
    get de(): DeDriverLicenseBack.Categories.DE {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'DE');
      return new DeDriverLicenseBack.Categories.DE(child as GenericDocument);
    }

    /** The child document of type "L". */
    get l(): DeDriverLicenseBack.Categories.L {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'L');
      return new DeDriverLicenseBack.Categories.L(child as GenericDocument);
    }

    /** The child document of type "T". */
    get t(): DeDriverLicenseBack.Categories.T {
      const child = this.document.children.find((c: GenericDocument) => c.type.name === 'T');
      return new DeDriverLicenseBack.Categories.T(child as GenericDocument);
    }
  }

  export namespace Categories {
    /** DeDriverLicenseBack.Categories.A */
    export class A extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesADocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.A1 */
    export class A1 extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesA1DocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.A2 */
    export class A2 extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesA2DocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.AM */
    export class AM extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesAMDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.B */
    export class B extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesBDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.B1 */
    export class B1 extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesB1DocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.BE */
    export class BE extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesBEDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.C */
    export class C extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesCDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.C1 */
    export class C1 extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesC1DocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.C1E */
    export class C1E extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesC1EDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.CE */
    export class CE extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesCEDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.D */
    export class D extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesDDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.D1 */
    export class D1 extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesD1DocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.D1E */
    export class D1E extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesD1EDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.DE */
    export class DE extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesDEDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.L */
    export class L extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesLDocumentType;
      }
    }

    /** DeDriverLicenseBack.Categories.T */
    export class T extends Category {
      constructor(document: GenericDocument) {
        super(document);
      }

      requiredDocumentType(): string {
        return DeDriverLicenseBackCategoriesTDocumentType;
      }
    }
  }
}

/** German Residence Permit (Aufenthaltstitel), Front side */
export class DeResidencePermitFront {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeResidencePermitFrontDocumentType;
  }

  /** Birth date (Geburtsdatum) */
  get birthDate(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate');
  }

  /** Six digit card access number */
  get cardAccessNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'CardAccessNumber') as Field;
  }

  /** Expiry date (Gültig bis) */
  get expiryDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate') as Field;
  }

  /** Gender (Geschlecht) */
  get gender(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Gender');
  }

  /** Given names */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Document ID number (in the top-right corner) */
  get id(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ID') as Field;
  }

  /** Nationality (Staatsangehörigkeit) */
  get nationality(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Nationality');
  }

  /** Photo image */
  get photo(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Photo') as Field;
  }

  /** Place of issue (Ausstellungsort) */
  get placeOfIssue(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'PlaceOfIssue');
  }

  /** Remarks (Anmerkungen) */
  get remarks(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Remarks') as Field;
  }

  /** Signature image */
  get signature(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Signature') as Field;
  }

  /** Surname */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }

  /** Title type (Art des Titels) */
  get titleType(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'TitleType') as Field;
  }

  /** Valid from date (Gültig ab) */
  get validFrom(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'ValidFrom');
  }
}

/** German Residence Permit (Aufenthaltstitel), Back side */
export class DeResidencePermitBack {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeResidencePermitBackDocumentType;
  }

  /** Address (Anschrift) */
  get address(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Address') as Field;
  }

  /** Birth date (Geburtsdatum) */
  get birthDate(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate');
  }

  /** Birthplace (Geburtsort) */
  get birthplace(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Birthplace') as Field;
  }

  /** Eye color (Augenfarbe) */
  get eyeColor(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'EyeColor') as Field;
  }

  /** Gender (Geschlecht) */
  get gender(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Gender');
  }

  /** Height (Größe) */
  get height(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Height') as Field;
  }

  /** Issuing authority (Ausländerbehörde) */
  get issuingAuthority(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuingAuthority') as Field;
  }

  /** Nationality (Staatsangehörigkeit) */
  get nationality(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Nationality');
  }

  /** Raw MRZ text value */
  get rawMRZ(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RawMRZ') as Field;
  }

  /** Remarks (Anmerkungen) */
  get remarks(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Remarks');
  }

  /** The child document of type "MRZ". */
  get mrz(): MRZ {
    const child = this.document.children.find((c: GenericDocument) => c.type.name === 'MRZ');
    return new MRZ(child as GenericDocument);
  }
}

export namespace DeResidencePermitBack {}

/** European Health Insurance Card (EHIC). Supports formats with both four and five lines of data.
 */
export class EuropeanHealthInsuranceCard {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return EuropeanHealthInsuranceCardDocumentType;
  }

  /** Barcode image (only present in some formats) */
  get barcode(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Barcode');
  }

  /** Birth date */
  get birthDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BirthDate') as Field;
  }

  /** Card number */
  get cardNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'CardNumber') as Field;
  }

  /** Country code (ISO 3166-1 alpha-2) */
  get countryCode(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'CountryCode') as Field;
  }

  /** Expiry date */
  get expiryDate(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ExpiryDate') as Field;
  }

  /** Given names */
  get givenNames(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'GivenNames') as Field;
  }

  /** Issuer name */
  get issuerName(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuerName') as Field;
  }

  /** Issuer number */
  get issuerNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuerNumber') as Field;
  }

  /** Personal number */
  get personalNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'PersonalNumber') as Field;
  }

  /** Signature image */
  get signature(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'Signature');
  }

  /** Surname */
  get surname(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Surname') as Field;
  }
}

/** Front side of the German health insurance card (elektronische Gesundheitskarte). 
For the backside, see EuropeanHealthInsuranceCard.
 */
export class DeHealthInsuranceCardFront {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  requiredDocumentType(): string {
    return DeHealthInsuranceCardFrontDocumentType;
  }

  /** Six digit card access number */
  get cardAccessNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'CardAccessNumber');
  }

  /** Issuer name */
  get issuerName(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuerName') as Field;
  }

  /** Issuer number (Versicherung bzw. Kennnummer des Trägers) */
  get issuerNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'IssuerNumber') as Field;
  }

  /** Full name with title */
  get name(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'Name') as Field;
  }

  /** Personal number (Versichertennummer) */
  get personalNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'PersonalNumber') as Field;
  }
}
