// mathtext - A TeX/LaTeX compatible rendering library. Copyright (C)
// 2008-2012 Yue Shi Lai <ylai@users.sourceforge.net>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either version 2.1 of
// the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA

#include <math.h>
#include <algorithm>
#include "../inc/mathrender.h"

/////////////////////////////////////////////////////////////////////

namespace mathtext {

   /////////////////////////////////////////////////////////////////

   float math_text_renderer_t::
   style_size(const unsigned int style) const
   {
      // Intel C++ Compiler 10.1 chokes on this if declared static
      // const and using -O1:
      const float size[math_text_t::item_t::NSTYLE - 1] = {
         script_script_ratio, script_script_ratio,
         script_ratio, script_ratio, 1.0F, 1.0F, 1.0F, 1.0F
      };

      if(style == math_text_t::item_t::STYLE_UNKNOWN ||
         style >= math_text_t::item_t::NSTYLE)
         return font_size();

      return size[style - 1] * font_size();
   }

   bool math_text_renderer_t::
   is_display_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_DISPLAY:
         case math_text_t::item_t::STYLE_DISPLAY_PRIME:
            return true;
         default:
            return false;
      }
   }

   bool math_text_renderer_t::
   is_script_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_SCRIPT:
         case math_text_t::item_t::STYLE_SCRIPT_PRIME:
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT:
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT_PRIME:
            return true;
         default:
            return false;
      }
   }

   unsigned int math_text_renderer_t::
   prime_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_DISPLAY:
            return math_text_t::item_t::STYLE_DISPLAY_PRIME;
         case math_text_t::item_t::STYLE_TEXT:
            return math_text_t::item_t::STYLE_TEXT_PRIME;
         case math_text_t::item_t::STYLE_SCRIPT:
            return math_text_t::item_t::STYLE_SCRIPT_PRIME;
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT:
            return math_text_t::item_t::STYLE_SCRIPT_SCRIPT_PRIME;
         default:
            return style;
      }
   }

   bool math_text_renderer_t::
   is_prime_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_DISPLAY_PRIME:
         case math_text_t::item_t::STYLE_TEXT_PRIME:
         case math_text_t::item_t::STYLE_SCRIPT_PRIME:
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT_PRIME:
            return true;
         default:
            return false;
      }
   }

   // Knuth, The TeXbook (1986), p. 441

   unsigned int math_text_renderer_t::
   next_superscript_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_DISPLAY:
         case math_text_t::item_t::STYLE_TEXT:
            return math_text_t::item_t::STYLE_SCRIPT;
         case math_text_t::item_t::STYLE_DISPLAY_PRIME:
         case math_text_t::item_t::STYLE_TEXT_PRIME:
            return math_text_t::item_t::STYLE_SCRIPT_PRIME;
         case math_text_t::item_t::STYLE_SCRIPT:
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT:
            return math_text_t::item_t::STYLE_SCRIPT_SCRIPT;
         case math_text_t::item_t::STYLE_SCRIPT_PRIME:
         case math_text_t::item_t::STYLE_SCRIPT_SCRIPT_PRIME:
            return math_text_t::item_t::STYLE_SCRIPT_SCRIPT_PRIME;
         default:
            return style;
      }
   }

   unsigned int math_text_renderer_t::
   next_subscript_style(const unsigned int style) const
   {
      return prime_style(next_superscript_style(style));
   }

   unsigned int math_text_renderer_t::
   next_numerator_style(const unsigned int style) const
   {
      switch(style) {
         case math_text_t::item_t::STYLE_DISPLAY:
            return math_text_t::item_t::STYLE_TEXT;
         case math_text_t::item_t::STYLE_DISPLAY_PRIME:
            return math_text_t::item_t::STYLE_TEXT_PRIME;
         default:
            return next_superscript_style(style);
      }
   }

   unsigned int math_text_renderer_t::
   next_denominator_style(const unsigned int style) const
   {
      return if_else_display(
                             style,
                             static_cast<unsigned int>(
                                                       math_text_t::item_t::STYLE_TEXT_PRIME),
                             next_subscript_style(style));
   }

   // The most elementary building block is the math symbol
   float math_text_renderer_t::x_height(const unsigned int style)
   {
      const unsigned int family = FAMILY_ITALIC;
      const float size = style_size(style);

      set_font_size(size, family);

      bounding_box_t math_symbol_bounding_box =
      bounding_box(L"x", family);

      reset_font_size(family);

      return math_symbol_bounding_box.ascent();
   }

   float math_text_renderer_t::quad(const unsigned int style)
   const
   {
      const float size = style_size(style);

      return size;
   }

   void math_text_renderer_t::
   post_process_atom_type_initial(unsigned int &atom_type) const
   {
      // Rule 5, initial atom
      if(atom_type == math_text_t::atom_t::TYPE_BIN)
         atom_type = math_text_t::atom_t::TYPE_ORD;
   }

   void math_text_renderer_t::
   post_process_atom_type_interior(unsigned int &
                                   previous_atom_type,
                                   unsigned int &atom_type) const
   {
      // Rule 5, interior/final atom
      if(atom_type == math_text_t::atom_t::TYPE_BIN)
         switch(previous_atom_type) {
            case math_text_t::atom_t::TYPE_BIN:
            case math_text_t::atom_t::TYPE_OP:
            case math_text_t::atom_t::TYPE_REL:
            case math_text_t::atom_t::TYPE_OPEN:
            case math_text_t::atom_t::TYPE_PUNCT:
               atom_type = math_text_t::atom_t::TYPE_ORD;
               break;
         }
      // Rule 6
      else if(previous_atom_type == math_text_t::atom_t::TYPE_BIN)
         switch(atom_type) {
            case math_text_t::atom_t::TYPE_REL:
            case math_text_t::atom_t::TYPE_CLOSE:
            case math_text_t::atom_t::TYPE_PUNCT:
               previous_atom_type = math_text_t::atom_t::TYPE_ORD;
               break;
         }
   }

   bool math_text_renderer_t::
   valid_accent(bool &vertical_alignment,
                const std::vector<math_text_t::item_t>::
                const_iterator &iterator,
                const std::vector<math_text_t::item_t>::
                const_iterator &math_list_end)
   const
   {
      if(iterator->_atom._type == math_text_t::atom_t::TYPE_ACC) {
         std::vector<math_text_t::item_t>::const_iterator
         iterator_next = iterator + 1;

         vertical_alignment = true;
         return iterator_next != math_list_end &&
         iterator_next->_type ==
         math_text_t::item_t::TYPE_ATOM;
      }
      else if(iterator->_atom.is_combining_diacritical()) {
         std::vector<math_text_t::item_t>::const_iterator
         iterator_next = iterator + 1;

         vertical_alignment = false;
         return iterator_next != math_list_end &&
         iterator_next->_type ==
         math_text_t::item_t::TYPE_ATOM;
      }
      else
         return false;
   }

   float math_text_renderer_t::kerning_mu(const float amount) const
   {
      // Rule 2
      return amount / 18.0F * font_size();
   }

   float math_text_renderer_t::
   math_spacing(unsigned int left_type, unsigned int right_type,
                unsigned int style) const
   {
      const unsigned int left_type_modified =
      left_type <= (unsigned int) math_text_t::atom_t::TYPE_INNER ?
      left_type : (unsigned int) math_text_t::atom_t::TYPE_ORD;
      const unsigned int right_type_modified =
      right_type <= (unsigned int) math_text_t::atom_t::TYPE_INNER ?
      right_type : (unsigned int) math_text_t::atom_t::TYPE_ORD;
      const unsigned int space = math_text_t::atom_t::
      spacing(left_type_modified, right_type_modified,
              is_script_style(style));
      float mu_skip;

      switch(space) {
         case 1:      mu_skip = thin_mu_skip; break;
         case 2:      mu_skip = med_mu_skip; break;
         case 3:      mu_skip = thick_mu_skip; break;
         default:   mu_skip = 0.0F;
      }

      return kerning_mu(mu_skip);
   }

   unsigned int math_text_renderer_t::
   math_family(const math_text_t::math_symbol_t &math_symbol) const
   {
      // Use the text font for Latin, Greek, Cyrillic and the minus
      // sign, and STIX for everything else.
      if(math_symbol._glyph <= L'\u017e' ||
         (math_symbol._glyph >= L'\u0384' &&
          math_symbol._glyph <= L'\u03ce') ||
         (math_symbol._glyph >= L'\u0400' &&
          math_symbol._glyph <= L'\u052f') ||
         math_symbol._glyph == L'\u2212') {
         return math_symbol._family;
      }
      else {
         switch(math_symbol._family) {
            case FAMILY_REGULAR:
               return FAMILY_STIX_REGULAR;
            case FAMILY_ITALIC:
               return FAMILY_STIX_ITALIC;
            case FAMILY_BOLD:
               return FAMILY_STIX_BOLD;
            case FAMILY_BOLD_ITALIC:
               return FAMILY_STIX_BOLD_ITALIC;
            case FAMILY_STIX_REGULAR:
            case FAMILY_STIX_ITALIC:
            case FAMILY_STIX_BOLD:
            case FAMILY_STIX_BOLD_ITALIC:
            case FAMILY_STIX_SIZE_1_REGULAR:
            case FAMILY_STIX_SIZE_1_BOLD:
            case FAMILY_STIX_SIZE_2_REGULAR:
            case FAMILY_STIX_SIZE_2_BOLD:
            case FAMILY_STIX_SIZE_3_REGULAR:
            case FAMILY_STIX_SIZE_3_BOLD:
            case FAMILY_STIX_SIZE_4_REGULAR:
            case FAMILY_STIX_SIZE_4_BOLD:
            case FAMILY_STIX_SIZE_5_REGULAR:
               return math_symbol._family;
            default:
               return FAMILY_STIX_REGULAR;
         }
      }
   }

   void math_text_renderer_t::
   large_family(unsigned long &nfamily, const unsigned int *&family,
                const math_text_t::math_symbol_t &math_symbol) const
   {
      static const unsigned long nlarge_family = 5;
      static const unsigned int
      large_family_regular[nlarge_family] = {
         FAMILY_STIX_REGULAR,
         FAMILY_STIX_SIZE_1_REGULAR,
         FAMILY_STIX_SIZE_2_REGULAR,
         FAMILY_STIX_SIZE_3_REGULAR,
         FAMILY_STIX_SIZE_4_REGULAR,
      };
      static const unsigned int
      large_family_bold[nlarge_family] = {
         FAMILY_STIX_BOLD,
         FAMILY_STIX_SIZE_1_BOLD,
         FAMILY_STIX_SIZE_2_BOLD,
         FAMILY_STIX_SIZE_3_BOLD,
         FAMILY_STIX_SIZE_4_BOLD,
      };

      nfamily = nlarge_family;
      family = math_symbol.bold() ?
      large_family_bold : large_family_regular;
   }

   void math_text_renderer_t::
   extensible_glyph(wchar_t glyph[4], unsigned long &nrepeat,
                    const math_text_t::math_symbol_t &math_symbol,
                    const unsigned int style, const float height)
   {
      // See Knuth, The METAFONTbook (1986), p. 318
      enum {
         GLYPH_TOP = 0,
         GLYPH_MIDDLE,
         GLYPH_BOTTOM,
         GLYPH_REPEATABLE,
         NGLYPH
      };

      switch(math_symbol._glyph) {
         case L'(':
            glyph[GLYPH_TOP] = L'\u239b';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u239d';
            glyph[GLYPH_REPEATABLE] = L'\u239c';
            break;
         case L')':
            glyph[GLYPH_TOP] = L'\u239e';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u23a0';
            glyph[GLYPH_REPEATABLE] = L'\u239f';
            break;
         case L'[':
            glyph[GLYPH_TOP] = L'\u23a1';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u23a3';
            glyph[GLYPH_REPEATABLE] = L'\u23a2';
            break;
         case L']':
            glyph[GLYPH_TOP] = L'\u23a4';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u23a6';
            glyph[GLYPH_REPEATABLE] = L'\u23a5';
            break;
         case L'{':
            glyph[GLYPH_TOP] = L'\u23a7';
            glyph[GLYPH_MIDDLE] = L'\u23a8';
            glyph[GLYPH_BOTTOM] = L'\u23a9';
            glyph[GLYPH_REPEATABLE] = L'\u23aa';
            break;
         case L'|':
            glyph[GLYPH_TOP] = math_symbol._glyph;
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = math_symbol._glyph;
            glyph[GLYPH_REPEATABLE] = math_symbol._glyph;
            break;
         case L'}':
            glyph[GLYPH_TOP] = L'\u23ab';
            glyph[GLYPH_MIDDLE] = L'\u23ac';
            glyph[GLYPH_BOTTOM] = L'\u23ad';
            glyph[GLYPH_REPEATABLE] = L'\u23aa';
            break;
#if 0
            // FIXME: \lmoustache, \rmoustache, \radical require
            // horizontal offsets
         case L'\u211a':      // \lmoustache
            glyph[GLYPH_TOP] = L'\0';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u23b7';
            glyph[GLYPH_REPEATABLE] = L'\u23b9';
            break;
         case L'\u23b0':      // \rmoustache
            glyph[GLYPH_TOP] = L'\u239b';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u23a0';
            glyph[GLYPH_REPEATABLE] = L'\u239c';
            break;
         case L'\u23b1':      // \radical
            glyph[GLYPH_TOP] = L'\u239e';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\u239d';
            glyph[GLYPH_REPEATABLE] = L'\u239f';
            break;
#endif
         default:
            glyph[GLYPH_TOP] = L'\0';
            glyph[GLYPH_MIDDLE] = L'\0';
            glyph[GLYPH_BOTTOM] = L'\0';
            glyph[GLYPH_REPEATABLE] = L'\0';
      }

      const unsigned int family = math_symbol._glyph == L'|' ?
      FAMILY_STIX_REGULAR : FAMILY_STIX_SIZE_1_REGULAR;
      const float size = style_size(style);

      if(glyph[GLYPH_REPEATABLE] != L'\0') {
         bounding_box_t bounding_box_sum(0, 0, 0, 0, 0, 0);
         float current_y = 0;

         for(unsigned long i = GLYPH_TOP; i <= GLYPH_BOTTOM;
             i++) {
            if(glyph[i] != L'\0') {
               bounding_box_t glyph_bounding_box =
               math_bounding_box(glyph[i], family, size);

               current_y += glyph_bounding_box.descent();
               bounding_box_sum = bounding_box_sum.
               merge(point_t(0, current_y) +
                     glyph_bounding_box);
               current_y += glyph_bounding_box.ascent();
            }
         }
         
         const bounding_box_t bounding_box_repeatable =
         math_bounding_box(glyph[GLYPH_REPEATABLE], family,
                           size);
         const float remaining_height =
         height - bounding_box_sum.height();
         unsigned long repeat_ratio = 0;
         if (bounding_box_repeatable.height() !=0) {
            repeat_ratio =
            (unsigned long)ceil(
                                remaining_height /
                                bounding_box_repeatable.height());
         }
         nrepeat = glyph[GLYPH_MIDDLE] == L'\0' ?
         repeat_ratio : ((repeat_ratio + 1UL) >> 1);
      }
      else
         nrepeat = 0;
   }
   
   // Font parameters
#include "table/mathfontparam.h"
   
}
