/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2012 Kitware, Inc.

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/

#include "cmWIXRichTextFormatWriter.h"

#include <cmVersion.h>

cmWIXRichTextFormatWriter::cmWIXRichTextFormatWriter(
  std::string const& filename):
    File(filename.c_str(), std::ios::binary)
{
  StartGroup();
  WriteHeader();
  WriteDocumentPrefix();
}

cmWIXRichTextFormatWriter::~cmWIXRichTextFormatWriter()
{
  EndGroup();

  /* I haven't seen this in the RTF spec but
   *  wordpad terminates its RTF like this */
  File << "\r\n";
  File.put(0);
}

void cmWIXRichTextFormatWriter::AddText(std::string const& text)
{
  typedef unsigned char rtf_byte_t;

  for(size_t i = 0; i < text.size(); ++i)
    {
    rtf_byte_t c = rtf_byte_t(text[i]);

    switch(c)
      {
    case '\\':
      File << "\\\\";
      break;
    case '{':
      File << "\\{";
      break;
    case '}':
      File << "\\}";
      break;
    case '\n':
      File << "\\par\r\n";
      break;
    case '\r':
      continue;
    default:
        {
        if(c <= 0x7F)
          {
          File << c;
          }
        else
          {
            if(c <= 0xC0)
              {
              EmitInvalidCodepoint(c);
              }
            else if(c < 0xE0 && i+1 < text.size())
              {
              EmitUnicodeCodepoint(
                (text[i+1] & 0x3F) |
                ((c & 0x1F) << 6)
              );
              i+= 1;
              }
            else if(c < 0xF0 && i+2 < text.size())
              {
              EmitUnicodeCodepoint(
                (text[i+2] & 0x3F) |
                ((text[i+1] & 0x3F) << 6) |
                ((c & 0xF) << 12)
              );
              i += 2;
              }
            else if(c < 0xF8 && i+3 < text.size())
              {
              EmitUnicodeCodepoint(
                (text[i+3] & 0x3F) |
                ((text[i+2] & 0x3F) << 6) |
                ((text[i+1] & 0x3F) << 12) |
                ((c & 0x7) << 18)
              );
              i += 3;
              }
            else
              {
              EmitInvalidCodepoint(c);
              }
          }
        }
      break;
      }
    }
}

void cmWIXRichTextFormatWriter::WriteHeader()
{
  ControlWord("rtf1");
  ControlWord("ansi");
  ControlWord("ansicpg1252");
  ControlWord("deff0");
  ControlWord("deflang1031");

  WriteFontTable();
  WriteColorTable();
  WriteGenerator();
}

void cmWIXRichTextFormatWriter::WriteFontTable()
{
  StartGroup();
  ControlWord("fonttbl");

  StartGroup();
  ControlWord("f0");
  ControlWord("fswiss");
  ControlWord("fcharset0 Arial;");
  EndGroup();

  EndGroup();
}

void cmWIXRichTextFormatWriter::WriteColorTable()
{
  StartGroup();
  ControlWord("colortbl ;");
  ControlWord("red255");
  ControlWord("green0");
  ControlWord("blue0;");
  ControlWord("red0");
  ControlWord("green255");
  ControlWord("blue0;");
  ControlWord("red0");
  ControlWord("green0");
  ControlWord("blue255;");
  EndGroup();
}

void cmWIXRichTextFormatWriter::WriteGenerator()
{
  StartGroup();
  NewControlWord("generator");
  File << " CPack WiX Generator (" << cmVersion::GetCMakeVersion() << ");";
  EndGroup();
}

void cmWIXRichTextFormatWriter::WriteDocumentPrefix()
{
  ControlWord("viewkind4");
  ControlWord("uc1");
  ControlWord("pard");
  ControlWord("f0");
  ControlWord("fs20");
}

void cmWIXRichTextFormatWriter::ControlWord(std::string const& keyword)
{
  File << "\\" << keyword;
}

void cmWIXRichTextFormatWriter::NewControlWord(std::string const& keyword)
{
  File << "\\*\\" << keyword;
}

void cmWIXRichTextFormatWriter::StartGroup()
{
  File.put('{');
}

void cmWIXRichTextFormatWriter::EndGroup()
{
  File.put('}');
}

void cmWIXRichTextFormatWriter::EmitUnicodeCodepoint(int c)
{
  // Do not emit byte order mark (BOM)
  if(c == 0xFEFF)
    {
    return;
    }
  else if(c <= 0xFFFF)
    {
    EmitUnicodeSurrogate(c);
    }
  else
    {
    c -= 0x10000;
    EmitUnicodeSurrogate(((c >> 10) & 0x3FF) + 0xD800);
    EmitUnicodeSurrogate((c & 0x3FF) + 0xDC00);
    }
}

void cmWIXRichTextFormatWriter::EmitUnicodeSurrogate(int c)
{
  ControlWord("u");
  if(c <= 32767)
    {
    File << c;
    }
  else
    {
    File << (c - 65536);
    }
  File << "?";
}

void cmWIXRichTextFormatWriter::EmitInvalidCodepoint(int c)
{
  ControlWord("cf1 ");
  File << "[INVALID-BYTE-" << int(c) << "]";
  ControlWord("cf0 ");
}