/* This file is part of Zutty.
 * Copyright (C) 2020 Tom Szilagyi
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * See the file LICENSE for the full license.
 */

#pragma once

#include <ft2build.h>
#include FT_FREETYPE_H

#include <cstdint>
#include <string>
#include <vector>
#include <map>

namespace zutty
{
   class Font
   {
   public:
      enum Overlay_ { Overlay };
      enum DoubleWidth_ { DoubleWidth };

      /* Load a primary font, determining the atlas geometry and setting up
       * a mapping from unicode code points to atlas grid positions.
       */
      explicit Font (const std::string& filename);

      /* Load an alternate font based on an already loaded primary font,
       * conforming to the same atlas geometry (incl. position mapping)
       * and starting with a copy of the atlas texture data.
       *
       * It is an error if the alternate font has different geometry.
       * Any code point not having a glyph in the alternate font will
       * have the glyph of the primary font (if any) in its atlas.
       * Any code point not having a glyph in the primary font will be
       * discarded.
       */
      Font (const std::string& filename, const Font& priFont, Overlay_);

      /* Load a double-width font based on an already loaded primary font.
       * A double-width font is less tightly coupled to the primary,
       * but its glyph size has to match (double width, equal height).
       * The font will have its own independent atlas geometry.
       *
       * Only code points that are considered double-width by wcwidth ()
       * will be loaded.
       */
      Font (const std::string& filename, const Font& priFont, DoubleWidth_);

      ~Font () = default;

      uint16_t getPx () const { return px; };
      uint16_t getPy () const { return py; };
      uint16_t getBaseline () const { return baseline; };
      uint16_t getNx () const { return nx; };
      uint16_t getNy () const { return ny; };
      float getUlTop () const { return ulTop; };
      float getUlThick () const { return ulThick; };

      const std::vector <uint8_t>& getAtlas () const { return atlasBuf; };
      const uint8_t* getAtlasData () const { return atlasBuf.data (); };

      struct AtlasPos
      {
         uint8_t x = 0;
         uint8_t y = 0;
      };
      using AtlasMap = std::map <uint16_t, AtlasPos>;
      const AtlasMap& getAtlasMap () const { return atlasMap; };

   private:
      std::string filename;
      bool overlay = false;
      bool dwidth = false;
      uint16_t px = 0; // glyph width in pixels
      uint16_t py = 0; // glyph height in pixels
      uint16_t baseline = 0; // number of pixels above baseline
      float ulTop = 0;   // underline top from glyph top in pixels
      float ulThick = 0; // underline thickness in pixels
      uint16_t nx = 0; // number of glyphs in atlas texture per row
      uint16_t ny = 0; // number of rows in atlas texture
      std::vector <uint8_t> atlasBuf; // loaded atlas data
      AtlasMap atlasMap; // unicode -> atlas position

      /* Start with 1 so as to leave a blank glyph at (0,0).
       * That blank will get referenced for any out-of-bounds text position
       * lookup in the shader, and guarantees that no fractional glyphs will
       * be shown at the right and bottom edges.
       * Also, any glyph mapping lookup that results in (0,0) means that the
       * character code does not exist in the atlas.
       */
      uint16_t atlas_seq = 1;

      int loadSkipCount = 0;

      /* Load font from glyph bitmaps rasterized by FreeType.
       * Store the bitmaps into an atlas bitmap stored in atlasBuf.
       */
      bool isLoadableChar (FT_ULong c);
      void load ();
      void loadFixed (const FT_Face& face);
      void loadScaled (const FT_Face& face);
      void loadFace (const FT_Face& face, FT_ULong c);
      void loadFace (const FT_Face& face, FT_ULong c, const AtlasPos& apos);
   };

} // namespace zutty
