const Pdfix = require('./index');

import {
  PdfDoc,
  PdfTagsParams,
  PdfAccessibleParams,
  PdfHtmlConversion,
  PdfHtmlParams,
  PsStream,
  PdfAnnot,
  PdfPage,
  PdfRect,
  PdfTextAnnot,
  PdsStream,
  PdsContent,
  PdsDictionary,
  PdfMatrix,
  PdsImage,
  PdfGraphicState,
  PdfBookmark,
  PdfTiffConversion,
  PdfTiffParams,
  PdsArray,
  PdeWordList,
  PdfixSdk,
  PsMemoryStream
} from "PDFIX_WASM";

import {
  PdfAuthPlatform,
  PdfixEnum,
  PdfHtmlType,
  PdfAnnotSubtype,
  PdfImageFormat,
  PdfWordFinderAlgorithm
} from "./bin/node/pdfix_enums";

function streamToString(sdk: PdfixSdk, stream: PsStream): string {
  const ptr: number = sdk._malloc(stream.GetSize());
  stream.Read(0, ptr as any, stream.GetSize());
  let string: string = '';
  for (let i = 0; i < stream.GetSize(); i++) {
    string += String.fromCharCode((sdk as any).HEAP8[ptr + i])
  }
  return string;
}

describe('PdfixTsTest', () => {
  let pdfix: typeof Pdfix;
  let pdfixSdk: PdfixSdk;
  let pdfixVersion: Array<number> = [7, 1, 1];

  beforeAll(async () => {
    pdfix = new Pdfix();
    await pdfix.loadPdfixSdk();
    pdfixSdk = pdfix.getPdfixSdk();
  });

  afterAll(() => {
    pdfixSdk.GetPdfix().Destroy();
  });

  it('should create Pdfix instance', () => {
    expect(pdfix).toBeInstanceOf(Pdfix);
  });

  it('should check the PDFix version', () => {
    const versionMajor: number = pdfixSdk.GetPdfix().GetVersionMajor();
    const versionMinor: number = pdfixSdk.GetPdfix().GetVersionMinor();
    const versionPatch: number = pdfixSdk.GetPdfix().GetVersionPatch();
    expect(versionMajor).toBe(pdfixVersion[0]);
    expect(versionMinor).toBe(pdfixVersion[1]);
    expect(versionPatch).toBe(pdfixVersion[2]);
  });

  it('should touch enum values', () => {
    expect(PdfAuthPlatform.kAuthPlatformAndroid).toBe(3);
  });

  it('should open a document from path', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    expect(pdfDoc).toBeDefined();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should add Tags to PDF', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfTagsParams: PdfTagsParams = new pdfixSdk.PdfTagsParams();
    expect((pdfTagsParams as any).ptr).toBeTruthy();
    expect(pdfDoc.AddTags(pdfTagsParams, 0, 0)).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should make a PDF Accessible', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfAccessibleParams: PdfAccessibleParams = new pdfixSdk.PdfAccessibleParams();
    expect((pdfAccessibleParams as any).ptr).toBeTruthy();
    let result: boolean = pdfDoc.MakeAccessible(
      pdfAccessibleParams,
      "Document Title",
      "en-US",
      0,
      0
    );
    expect(result).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create PDF to HTML conversion and get document CSS', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfToHtmlConversion: PdfHtmlConversion = pdfDoc.CreateHtmlConversion();
    expect((pdfToHtmlConversion as any).ptr).toBeTruthy();
    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    expect(pdfToHtmlConversion.SaveCSS(stream)).toBeTruthy();
    const htmlString = streamToString(pdfixSdk, stream);
    stream.Destroy();
    pdfToHtmlConversion.Destroy();
    expect(htmlString).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create PDF to HTML conversion and get document JavaScript', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfToHtmlConversion: PdfHtmlConversion = pdfDoc.CreateHtmlConversion();
    expect((pdfToHtmlConversion as any).ptr).toBeTruthy();
    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    pdfToHtmlConversion.SaveJavaScript(stream);
    const htmlString = streamToString(pdfixSdk, stream);
    stream.Destroy();
    pdfToHtmlConversion.Destroy();
    expect(htmlString).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create PDF to HTML conversion and get document fixed HTML', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfToHtmlConversion: PdfHtmlConversion = pdfDoc.CreateHtmlConversion();
    expect((pdfToHtmlConversion as any).ptr).toBeTruthy();
    let pdfHtmlParams: PdfHtmlParams = new pdfixSdk.PdfHtmlParams();
    pdfHtmlParams.flags |= (
      PdfixEnum.kHtmlNoExternalCSS |
      PdfixEnum.kHtmlNoExternalJS |
      PdfixEnum.kHtmlNoExternalIMG |
      PdfixEnum.kHtmlNoExternalFONT |
      PdfixEnum.kHtmlNoPagesNode
    );
    pdfHtmlParams.type = PdfHtmlType.kPdfHtmlFixed;
    expect(pdfToHtmlConversion.SetParams(pdfHtmlParams)).toBeTruthy();
    expect(pdfToHtmlConversion.AddPage(0, 0, 0)).toBeTruthy();

    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    expect(pdfToHtmlConversion.SaveToStream(stream, 0, 0)).toBeTruthy();
    const htmlString = streamToString(pdfixSdk, stream);
    stream.Destroy();
    pdfToHtmlConversion.Destroy();

    expect(htmlString).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create PDF to HTML conversion and get document responsive HTML', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfToHtmlConversion: PdfHtmlConversion = pdfDoc.CreateHtmlConversion();
    expect((pdfToHtmlConversion as any).ptr).toBeTruthy();
    let pdfHtmlParams: PdfHtmlParams = new pdfixSdk.PdfHtmlParams();
    pdfHtmlParams.flags |= (
      PdfixEnum.kHtmlNoExternalCSS |
      PdfixEnum.kHtmlNoExternalJS |
      PdfixEnum.kHtmlNoExternalIMG |
      PdfixEnum.kHtmlNoExternalFONT
    );
    pdfHtmlParams.type = PdfHtmlType.kPdfHtmlResponsive;
    expect(pdfToHtmlConversion.SetParams(pdfHtmlParams)).toBeTruthy();
    expect(pdfToHtmlConversion.AddPage(1, 0, 0)).toBeTruthy();
    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    expect(pdfToHtmlConversion.SaveToStream(stream, 0, 0)).toBeTruthy();
    const htmlString = streamToString(pdfixSdk, stream);
    stream.Destroy();
    pdfToHtmlConversion.Destroy();

    expect(htmlString).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should remove & add tags, create PDF to HTML conversion and get document derived HTML', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    expect(pdfDoc.RemoveTags(0, 0)).toBeTruthy();
    const pdfTagsParams: PdfTagsParams = new pdfixSdk.PdfTagsParams();
    expect((pdfTagsParams as any).ptr).toBeTruthy();
    expect(pdfDoc.AddTags(pdfTagsParams, 0, 0)).toBeTruthy();
    const pdfToHtmlConversion: PdfHtmlConversion = pdfDoc.CreateHtmlConversion();
    expect((pdfToHtmlConversion as any).ptr).toBeTruthy();
    let pdfHtmlParams: PdfHtmlParams = new pdfixSdk.PdfHtmlParams();
    pdfHtmlParams.flags |= (
      PdfixEnum.kHtmlNoExternalCSS |
      PdfixEnum.kHtmlNoExternalJS |
      PdfixEnum.kHtmlNoExternalIMG |
      PdfixEnum.kHtmlNoExternalFONT |
      PdfixEnum.kHtmlNoPagesNode
    );
    pdfHtmlParams.type = PdfHtmlType.kPdfHtmlDerivation;
    expect(pdfToHtmlConversion.SetParams(pdfHtmlParams)).toBeTruthy();
    expect(pdfToHtmlConversion.AddPage(0, 0, 0)).toBeTruthy();

    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    expect(pdfToHtmlConversion.SaveToStream(stream, 0, 0)).toBeTruthy();
    const htmlString = streamToString(pdfixSdk, stream);
    stream.Destroy();
    pdfToHtmlConversion.Destroy();

    expect(htmlString).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create an annotation and add it to document', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfPage: PdfPage = pdfDoc.AcquirePage(0);
    expect((pdfPage as any).ptr).toBeTruthy();
    const rect: PdfRect = new pdfixSdk.PdfRect();
    expect((rect as any).ptr).toBeTruthy();
    const annot: PdfAnnot = pdfPage.CreateAnnot(PdfAnnotSubtype.kAnnotSquare, rect);
    expect((annot as any).ptr).toBeTruthy();
    expect(pdfPage.AddAnnot(pdfPage.GetNumAnnots(), annot)).toBeTruthy();
    expect(pdfPage.Release()).toBe(0);
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should add a comment, save, open newly saved document & verify num of annots', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfPage: PdfPage = pdfDoc.AcquirePage(0);
    expect((pdfPage as any).ptr).toBeTruthy();
    expect(pdfPage.GetNumAnnots()).toBe(3);
    const rect: PdfRect = new pdfixSdk.PdfRect();
    expect((rect as any).ptr).toBeTruthy();
    pdfPage.GetCropBox(rect);

    const annotRect: PdfRect = new pdfixSdk.PdfRect();
    expect((annotRect as any).ptr).toBeTruthy();
    annotRect.left = (rect.right + rect.left) / 2. - 10;
    annotRect.bottom = (rect.top + rect.bottom) / 2. - 10;
    annotRect.right = (rect.right + rect.left) / 2. + 10;
    annotRect.top = (rect.top + rect.bottom) / 2. + 10;

    const annot = pdfPage.CreateAnnot(PdfAnnotSubtype.kAnnotText, annotRect);
    expect((annot as any).ptr).toBeTruthy();
    expect(pdfPage.AddAnnot(pdfPage.GetNumAnnots(), annot)).toBeTruthy();

    const textAnnot: PdfTextAnnot = pdfixSdk.wrapPointer((annot as any).ptr, (pdfixSdk as any).PdfMarkupAnnot);
    expect(textAnnot.SetAuthor("Peter Brown")).toBeTruthy();
    expect(textAnnot.SetContents("This is my comment.")).toBeTruthy();
    expect(textAnnot.AddReply("Mark Fish", "This is some reply.")).toBeTruthy();

    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect(pdfDoc.SaveToStream(stream, PdfixEnum.kSaveFull)).toBeTruthy();
    const arrayBuffer: ArrayBuffer = new ArrayBuffer(stream.GetSize());
    (stream as any).ReadToArrayBuffer(0, arrayBuffer, arrayBuffer.byteLength);
    const buffer: Buffer = Buffer.from(arrayBuffer);
    const fs = require("fs");
    fs.writeFileSync("./pdf/test_out.pdf", buffer);

    expect(pdfPage.Release()).toBe(0);
    expect(pdfDoc.Close()).toBe(0);

    const pdfDocSaved: PdfDoc = pdfix.openDocumentFromPath("./pdf/test_out.pdf");
    const pdfPage1: PdfPage = pdfDocSaved.AcquirePage(0);
    expect(pdfPage1.GetNumAnnots()).toBe(4);
    expect(pdfPage1.Release()).toBe(0);
    expect(pdfDocSaved.Close()).toBe(0);

    fs.unlinkSync("./pdf/test_out.pdf");
  });

  it('should add a watermark', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();

    const memStream: PsMemoryStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((memStream as any).ptr).toBeTruthy();
    const fs = require("fs");
    const fileBuffer = new Uint8Array( fs.readFileSync('./pdf/watermark.png', null).buffer );
    const [buffer, size] = pdfixSdk.allocArray(fileBuffer);
    memStream.Write(0, buffer as any, size);

    const imageObj: PdsStream = pdfDoc.CreateXObjectFromImage(memStream, PdfImageFormat.kImageFormatPng, 0)
    expect((imageObj as any).ptr).toBeTruthy();

    for (let i = 0; i < pdfDoc.GetNumPages(); i++) {
      const pdfPage: PdfPage = pdfDoc.AcquirePage(i);
      expect((pdfPage as any).ptr).toBeTruthy();
      const content: PdsContent = pdfPage.GetContent();
      expect((content as any).ptr).toBeTruthy();

      const xObjDict: PdsDictionary = imageObj.GetStreamDict();
      expect((xObjDict as any)).toBeTruthy();
      const width: number = xObjDict.GetNumber("Width");
      const height: number = xObjDict.GetNumber("Height");
      expect(width).toBeTruthy();
      expect(height).toBeTruthy();

      const pageMatrix: PdfMatrix = new pdfixSdk.PdfMatrix();
      pdfPage.GetDefaultMatrix(pageMatrix);
      expect((pageMatrix as any).ptr).toBeTruthy();

      const cropRect: PdfRect = new pdfixSdk.PdfRect();
      pdfPage.GetCropBox(cropRect);
      expect((cropRect as any).ptr).toBeTruthy();

      const matrix: PdfMatrix = new pdfixSdk.PdfMatrix();
      expect((matrix as any).ptr).toBeTruthy();
      matrix.a = width;
      matrix.d = height;

      const addedImageObj: PdsImage = content.AddNewImage(PdfixEnum.kInsertAfterLast, imageObj, matrix);
      expect((addedImageObj as any).ptr).toBeTruthy();

      const graphicState: PdfGraphicState = new pdfixSdk.PdfGraphicState();
      expect((graphicState as any).ptr).toBeTruthy();
      expect(addedImageObj.GetGState(graphicState)).toBeTruthy();
      graphicState.color_state.fill_opacity = 0.5 * 255;
      addedImageObj.SetGState(graphicState);

      expect(pdfPage.SetContent()).toBeTruthy();
      expect(pdfPage.Release()).toBe(0);
    }

    expect(pdfDoc.Close()).toBe(0);
  });

  it('should get bookmark root, its child, num children and title', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();

    const bookmarkRook: PdfBookmark = pdfDoc.GetBookmarkRoot();
    expect((bookmarkRook as any).ptr).toBeTruthy();
    expect(bookmarkRook.GetNumChildren()).toBe(1);
    
    const firstChild: PdfBookmark = bookmarkRook.GetChild(0);
    expect((firstChild as any).ptr).toBeDefined();
    expect((firstChild as any).GetTitle()).toBe("Donec quis sollicitudin odio");

    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create PDF to TIFF conversion and convert to TIFF', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();

    const tiffConversion: PdfTiffConversion = pdfDoc.CreateTiffConversion();
    expect((tiffConversion as any).ptr).toBeTruthy();

    const params: PdfTiffParams = new pdfixSdk.PdfTiffParams();
    expect((params as any).ptr).toBeTruthy();
    expect(tiffConversion.SetParams(params)).toBeTruthy();

    const stream: PsStream = pdfixSdk.GetPdfix().CreateMemStream();
    expect((stream as any).ptr).toBeTruthy();
    expect(tiffConversion.SaveToStream(stream, 0, 0)).toBeTruthy();
    stream.Destroy();

    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create a new document', () => {
    const pdfDoc: PdfDoc = pdfixSdk.GetPdfix().CreateDoc();
    expect((pdfDoc as any).ptr).toBeTruthy();

    const rect: PdfRect = new pdfixSdk.PdfRect();
    rect.right = 595;
    rect.top = 842;
    
    const pdfPage: PdfPage = pdfDoc.CreatePage(PdfixEnum.kInsertAfterLast, rect);
    expect((pdfPage as any).ptr).toBeTruthy();

    expect(pdfPage.Release()).toBe(0);
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should create a redaction mark and apply redaction', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect((pdfDoc as any).ptr).toBeTruthy();
    const pdfPage: PdfPage = pdfDoc.AcquirePage(0);
    expect((pdfPage as any).ptr).toBeTruthy();

    const rect: PdfRect = new pdfixSdk.PdfRect();
    rect.left = 100;
    rect.right = 200;
    rect.top = 200;
    rect.bottom = 100;
    const redactAnnot: PdfAnnot = pdfPage.CreateAnnot(PdfAnnotSubtype.kAnnotRedact, rect);
    expect((redactAnnot as any).ptr).toBeTruthy();

    redactAnnot.NotifyWillChange("IC");

    const obj: PdsDictionary = redactAnnot.GetObject();
    expect((obj as any).ptr).toBeTruthy();

    const outlineColor: PdsArray = obj.PutArray("OC");
    expect((outlineColor as any).ptr).toBeTruthy();
    expect(outlineColor.PutNumber(0, 1.0)).toBeTruthy();
    expect(outlineColor.PutNumber(1, 0.0)).toBeTruthy();
    expect(outlineColor.PutNumber(2, 0.0)).toBeTruthy();

    const innerColor: PdsArray = obj.PutArray("IC");
    expect((innerColor as any).ptr).toBeTruthy();
    expect(innerColor.PutNumber(0, 0.0)).toBeTruthy();
    expect(innerColor.PutNumber(1, 0.0)).toBeTruthy();
    expect(innerColor.PutNumber(2, 0.0)).toBeTruthy();

    expect(obj.PutString("DA", "1 1 0 RG 1 1 0 rg 0 Tc 0 Tw 100 Tz 0 TL 0 Ts 0 Tr /Helv 10 Tf")).toBeTruthy();
    expect(obj.PutString("OverlayText", "THIS WAS REDACTED ")).toBeTruthy();
    expect(obj.PutNumber("Q", 0)).toBeTruthy();

    redactAnnot.NotifyDidChange("IC", 0);
    expect(pdfPage.AddAnnot(PdfixEnum.kInsertAfterLast, redactAnnot)).toBeTruthy();
    expect(pdfDoc.ApplyRedaction(0, 0)).toBeTruthy();

    expect(pdfPage.Release()).toBe(0);
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should delete pages', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    expect(pdfDoc.DeletePages(0, 1, 0, 0)).toBeTruthy();
    expect((pdfDoc as any).ptr).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should get document metadata', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    const meta: PdsStream = pdfDoc.GetMetadata();
    expect((meta as any).ptr).toBeTruthy();
    expect((pdfDoc as any).GetInfo("Title")).toBe("Lorem Ipsum");
    expect((pdfDoc as any).ptr).toBeTruthy();
    expect(pdfDoc.Close()).toBe(0);
  });

  it('should get page text', () => {
    const pdfDoc: PdfDoc = pdfix.openDocumentFromPath('./pdf/test.pdf');
    const pdfPage: PdfPage = pdfDoc.AcquirePage(0);
    expect((pdfPage as any).ptr).toBeTruthy();

    const wordList: PdeWordList = pdfPage.AcquireWordList(
      PdfWordFinderAlgorithm.kWordFinderAlgBasic, PdfixEnum.kWordFinderDefault);
    expect((wordList as any).ptr).toBeTruthy();
    expect((wordList.GetWord(0) as any).GetText()).toBe("Donec");

    expect(wordList.Release()).toBe(0);
    expect(pdfPage.Release()).toBe(0);
    expect(pdfDoc.Close()).toBe(0);
  });

});