// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "core/fpdfapi/fpdf_font/include/cpdf_font.h"
#include "core/fpdfapi/fpdf_parser/include/fpdf_parser_decode.h"
#include "core/fpdfdoc/include/cpvt_word.h"
#include "core/fpdfdoc/include/ipvt_fontmap.h"
#include "fpdfsdk/fxedit/include/fx_edit.h"
#include "fpdfsdk/fxedit/include/fxet_edit.h"

CFX_ByteString GetPDFWordString(IPVT_FontMap* pFontMap,
                                int32_t nFontIndex,
                                uint16_t Word,
                                uint16_t SubWord) {
  CFX_ByteString sWord;
  if (CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex)) {
    if (SubWord > 0) {
      Word = SubWord;
    } else {
      uint32_t dwCharCode =
          pPDFFont->IsUnicodeCompatible()
              ? pPDFFont->CharCodeFromUnicode(Word)
              : pFontMap->CharCodeFromUnicode(nFontIndex, Word);

      if (dwCharCode > 0) {
        pPDFFont->AppendChar(sWord, dwCharCode);
        return sWord;
      }
    }

    pPDFFont->AppendChar(sWord, Word);
  }

  return sWord;
}

static CFX_ByteString GetWordRenderString(const CFX_ByteString& strWords) {
  if (strWords.GetLength() > 0)
    return PDF_EncodeString(strWords) + " Tj\n";

  return "";
}

static CFX_ByteString GetFontSetString(IPVT_FontMap* pFontMap,
                                       int32_t nFontIndex,
                                       FX_FLOAT fFontSize) {
  CFX_ByteTextBuf sRet;
  if (pFontMap) {
    CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
    if (sFontAlias.GetLength() > 0 && fFontSize > 0)
      sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
  }
  return sRet.MakeString();
}

CFX_ByteString IFX_Edit::GetEditAppearanceStream(
    IFX_Edit* pEdit,
    const CFX_FloatPoint& ptOffset,
    const CPVT_WordRange* pRange /* = NULL*/,
    FX_BOOL bContinuous /* = TRUE*/,
    uint16_t SubWord /* = 0*/) {
  CFX_ByteTextBuf sEditStream, sWords;

  CFX_FloatPoint ptOld(0.0f, 0.0f), ptNew(0.0f, 0.0f);
  int32_t nCurFontIndex = -1;

  IFX_Edit_Iterator* pIterator = pEdit->GetIterator();
  if (pRange)
    pIterator->SetAt(pRange->BeginPos);
  else
    pIterator->SetAt(0);

  CPVT_WordPlace oldplace;

  while (pIterator->NextWord()) {
    CPVT_WordPlace place = pIterator->GetAt();

    if (pRange && place.WordCmp(pRange->EndPos) > 0)
      break;

    if (bContinuous) {
      if (place.LineCmp(oldplace) != 0) {
        if (sWords.GetSize() > 0) {
          sEditStream << GetWordRenderString(sWords.MakeString());
          sWords.Clear();
        }

        CPVT_Word word;
        if (pIterator->GetWord(word)) {
          ptNew = CFX_FloatPoint(word.ptWord.x + ptOffset.x,
                                 word.ptWord.y + ptOffset.y);
        } else {
          CPVT_Line line;
          pIterator->GetLine(line);
          ptNew = CFX_FloatPoint(line.ptLine.x + ptOffset.x,
                                 line.ptLine.y + ptOffset.y);
        }

        if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
                      << " Td\n";

          ptOld = ptNew;
        }
      }

      CPVT_Word word;
      if (pIterator->GetWord(word)) {
        if (word.nFontIndex != nCurFontIndex) {
          if (sWords.GetSize() > 0) {
            sEditStream << GetWordRenderString(sWords.MakeString());
            sWords.Clear();
          }
          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
                                          word.fFontSize);
          nCurFontIndex = word.nFontIndex;
        }

        sWords << GetPDFWordString(pEdit->GetFontMap(), nCurFontIndex,
                                   word.Word, SubWord);
      }

      oldplace = place;
    } else {
      CPVT_Word word;
      if (pIterator->GetWord(word)) {
        ptNew = CFX_FloatPoint(word.ptWord.x + ptOffset.x,
                               word.ptWord.y + ptOffset.y);

        if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
                      << " Td\n";
          ptOld = ptNew;
        }

        if (word.nFontIndex != nCurFontIndex) {
          sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
                                          word.fFontSize);
          nCurFontIndex = word.nFontIndex;
        }

        sEditStream << GetWordRenderString(GetPDFWordString(
            pEdit->GetFontMap(), nCurFontIndex, word.Word, SubWord));
      }
    }
  }

  if (sWords.GetSize() > 0) {
    sEditStream << GetWordRenderString(sWords.MakeString());
    sWords.Clear();
  }

  CFX_ByteTextBuf sAppStream;
  if (sEditStream.GetSize() > 0) {
    int32_t nHorzScale = pEdit->GetHorzScale();
    if (nHorzScale != 100) {
      sAppStream << nHorzScale << " Tz\n";
    }

    FX_FLOAT fCharSpace = pEdit->GetCharSpace();
    if (!FX_EDIT_IsFloatZero(fCharSpace)) {
      sAppStream << fCharSpace << " Tc\n";
    }

    sAppStream << sEditStream;
  }

  return sAppStream.MakeString();
}

CFX_ByteString IFX_Edit::GetSelectAppearanceStream(
    IFX_Edit* pEdit,
    const CFX_FloatPoint& ptOffset,
    const CPVT_WordRange* pRange) {
  CFX_ByteTextBuf sRet;

  if (pRange && pRange->IsExist()) {
    IFX_Edit_Iterator* pIterator = pEdit->GetIterator();
    pIterator->SetAt(pRange->BeginPos);

    while (pIterator->NextWord()) {
      CPVT_WordPlace place = pIterator->GetAt();

      if (pRange && place.WordCmp(pRange->EndPos) > 0)
        break;

      CPVT_Word word;
      CPVT_Line line;
      if (pIterator->GetWord(word) && pIterator->GetLine(line)) {
        sRet << word.ptWord.x + ptOffset.x << " "
             << line.ptLine.y + line.fLineDescent << " " << word.fWidth << " "
             << line.fLineAscent - line.fLineDescent << " re\nf\n";
      }
    }
  }

  return sRet.MakeString();
}