/* Pango2
 * pango-gravity.c: Gravity routines
 *
 * Copyright (C) 2006, 2007 Red Hat Software
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include "pango-gravity.h"

#include <math.h>

/**
 * pango2_gravity_to_rotation:
 * @gravity: gravity to query, should not be %PANGO2_GRAVITY_AUTO
 *
 * Converts a `Pango2Gravity` value to its natural rotation in radians.
 *
 * Note that [method@Pango2.Matrix.rotate] takes angle in degrees, not radians.
 * So, to call [method@Pango2.Matrix,rotate] with the output of this function
 * you should multiply it by (180. / G_PI).
 *
 * Return value: the rotation value corresponding to @gravity.
 */
double
pango2_gravity_to_rotation (Pango2Gravity gravity)
{
  double rotation;

  g_return_val_if_fail (gravity != PANGO2_GRAVITY_AUTO, 0);

  switch (gravity)
    {
      default:
      case PANGO2_GRAVITY_AUTO: /* shut gcc up */
      case PANGO2_GRAVITY_SOUTH:
        rotation =  0;
        break;
      case PANGO2_GRAVITY_NORTH:
        rotation =  G_PI;
        break;
      case PANGO2_GRAVITY_EAST:
        rotation = -G_PI_2;
        break;
      case PANGO2_GRAVITY_WEST:
        rotation = +G_PI_2;
        break;
    }

  return rotation;
}

/**
 * pango2_gravity_get_for_matrix:
 * @matrix: (nullable): a `Pango2Matrix`
 *
 * Finds the gravity that best matches the rotation component
 * in a `Pango2Matrix`.
 *
 * Return value: the gravity of @matrix, which will never be
 * %PANGO2_GRAVITY_AUTO, or %PANGO2_GRAVITY_SOUTH if @matrix is %NULL
 */
Pango2Gravity
pango2_gravity_get_for_matrix (const Pango2Matrix *matrix)
{
  Pango2Gravity gravity;
  double x;
  double y;

  if (!matrix)
    return PANGO2_GRAVITY_SOUTH;

  x = matrix->xy;
  y = matrix->yy;

  if (fabs (x) > fabs (y))
    gravity = x > 0 ? PANGO2_GRAVITY_WEST : PANGO2_GRAVITY_EAST;
  else
    gravity = y < 0 ? PANGO2_GRAVITY_NORTH : PANGO2_GRAVITY_SOUTH;

  return gravity;
}



typedef enum
{
  PANGO2_VERTICAL_DIRECTION_NONE,
  PANGO2_VERTICAL_DIRECTION_TTB,
  PANGO2_VERTICAL_DIRECTION_BTT
} Pango2VerticalDirection;

typedef struct {
  /* Pango2Direction */
  guint8 horiz_dir;             /* Orientation in horizontal context */

  /* Pango2VerticalDirection */
  guint8 vert_dir;              /* Orientation in vertical context */

  /* Pango2Gravity */
  guint8 preferred_gravity;     /* Preferred context gravity */

  /* gboolean */
  guint8 wide;                  /* Whether script is mostly wide.
                                 * Wide characters are upright (ie.
                                 * not rotated) in foreign context */
} Pango2ScriptProperties;

#define NONE PANGO2_VERTICAL_DIRECTION_NONE
#define TTB  PANGO2_VERTICAL_DIRECTION_TTB
#define BTT  PANGO2_VERTICAL_DIRECTION_BTT

#define LTR  PANGO2_DIRECTION_LTR
#define RTL  PANGO2_DIRECTION_RTL
#define WEAK PANGO2_DIRECTION_WEAK_LTR

#define S PANGO2_GRAVITY_SOUTH
#define E PANGO2_GRAVITY_EAST
#define W PANGO2_GRAVITY_WEST

const Pango2ScriptProperties script_properties[] =
  {                             /* ISO 15924 code */
      {LTR, NONE, S, FALSE},    /* Zyyy */
      {LTR, NONE, S, FALSE},    /* Qaai */
      {RTL, NONE, S, FALSE},    /* Arab */
      {LTR, NONE, S, FALSE},    /* Armn */
      {LTR, NONE, S, FALSE},    /* Beng */
      {LTR, TTB,  E, TRUE },    /* Bopo */
      {LTR, NONE, S, FALSE},    /* Cher */
      {LTR, NONE, S, FALSE},    /* Qaac */
      {LTR, NONE, S, FALSE},    /* Cyrl (Cyrs) */
      {LTR, NONE, S, FALSE},    /* Dsrt */
      {LTR, NONE, S, FALSE},    /* Deva */
      {LTR, NONE, S, FALSE},    /* Ethi */
      {LTR, NONE, S, FALSE},    /* Geor (Geon, Geoa) */
      {LTR, NONE, S, FALSE},    /* Goth */
      {LTR, NONE, S, FALSE},    /* Grek */
      {LTR, NONE, S, FALSE},    /* Gujr */
      {LTR, NONE, S, FALSE},    /* Guru */
      {LTR, TTB,  E, TRUE },    /* Hani */
      {LTR, TTB,  E, TRUE },    /* Hang */
      {RTL, NONE, S, FALSE},    /* Hebr */
      {LTR, TTB,  E, TRUE },    /* Hira */
      {LTR, NONE, S, FALSE},    /* Knda */
      {LTR, TTB,  E, TRUE },    /* Kana */
      {LTR, NONE, S, FALSE},    /* Khmr */
      {LTR, NONE, S, FALSE},    /* Laoo */
      {LTR, NONE, S, FALSE},    /* Latn (Latf, Latg) */
      {LTR, NONE, S, FALSE},    /* Mlym */
      {WEAK,TTB,  W, FALSE},    /* Mong */
      {LTR, NONE, S, FALSE},    /* Mymr */
      {LTR, BTT,  W, FALSE},    /* Ogam */
      {LTR, NONE, S, FALSE},    /* Ital */
      {LTR, NONE, S, FALSE},    /* Orya */
      {LTR, NONE, S, FALSE},    /* Runr */
      {LTR, NONE, S, FALSE},    /* Sinh */
      {RTL, NONE, S, FALSE},    /* Syrc (Syrj, Syrn, Syre) */
      {LTR, NONE, S, FALSE},    /* Taml */
      {LTR, NONE, S, FALSE},    /* Telu */
      {RTL, NONE, S, FALSE},    /* Thaa */
      {LTR, NONE, S, FALSE},    /* Thai */
      {LTR, NONE, S, FALSE},    /* Tibt */
      {LTR, NONE, S, FALSE},    /* Cans */
      {LTR, TTB,  S, TRUE },    /* Yiii */
      {LTR, NONE, S, FALSE},    /* Tglg */
      {LTR, NONE, S, FALSE},    /* Hano */
      {LTR, NONE, S, FALSE},    /* Buhd */
      {LTR, NONE, S, FALSE},    /* Tagb */

      /* Unicode-4.0 additions */
      {LTR, NONE, S, FALSE},    /* Brai */
      {RTL, NONE, S, FALSE},    /* Cprt */
      {LTR, NONE, S, FALSE},    /* Limb */
      {LTR, NONE, S, FALSE},    /* Osma */
      {LTR, NONE, S, FALSE},    /* Shaw */
      {LTR, NONE, S, FALSE},    /* Linb */
      {LTR, NONE, S, FALSE},    /* Tale */
      {LTR, NONE, S, FALSE},    /* Ugar */

      /* Unicode-4.1 additions */
      {LTR, NONE, S, FALSE},    /* Talu */
      {LTR, NONE, S, FALSE},    /* Bugi */
      {LTR, NONE, S, FALSE},    /* Glag */
      {LTR, NONE, S, FALSE},    /* Tfng */
      {LTR, NONE, S, FALSE},    /* Sylo */
      {LTR, NONE, S, FALSE},    /* Xpeo */
      {RTL, NONE, S, FALSE},    /* Khar */

      /* Unicode-5.0 additions */
      {LTR, NONE, S, FALSE},    /* Zzzz */
      {LTR, NONE, S, FALSE},    /* Bali */
      {LTR, NONE, S, FALSE},    /* Xsux */
      {RTL, NONE, S, FALSE},    /* Phnx */
      {LTR, NONE, S, FALSE},    /* Phag */
      {RTL, NONE, S, FALSE},    /* Nkoo */

      /* Unicode-5.1 additions */
      {LTR, NONE, S, FALSE},    /* Kali */
      {LTR, NONE, S, FALSE},    /* Lepc */
      {LTR, NONE, S, FALSE},    /* Rjng */
      {LTR, NONE, S, FALSE},    /* Sund */
      {LTR, NONE, S, FALSE},    /* Saur */
      {LTR, NONE, S, FALSE},    /* Cham */
      {LTR, NONE, S, FALSE},    /* Olck */
      {LTR, NONE, S, FALSE},    /* Vaii */
      {LTR, NONE, S, FALSE},    /* Cari */
      {LTR, NONE, S, FALSE},    /* Lyci */
      {RTL, NONE, S, FALSE},    /* Lydi */

      /* Unicode-5.2 additions */
      {RTL, NONE, S, FALSE},    /* Avst */
      {LTR, NONE, S, FALSE},    /* Bamu */
      {LTR, NONE, S, FALSE},    /* Egyp */
      {RTL, NONE, S, FALSE},    /* Armi */
      {RTL, NONE, S, FALSE},    /* Phli */
      {RTL, NONE, S, FALSE},    /* Prti */
      {LTR, NONE, S, FALSE},    /* Java */
      {LTR, NONE, S, FALSE},    /* Kthi */
      {LTR, NONE, S, FALSE},    /* Lisu */
      {LTR, NONE, S, FALSE},    /* Mtei */
      {RTL, NONE, S, FALSE},    /* Sarb */
      {RTL, NONE, S, FALSE},    /* Orkh */
      {RTL, TTB,  S, FALSE},    /* Samr */
      {LTR, NONE, S, FALSE},    /* Lana */
      {LTR, NONE, S, FALSE},    /* Tavt */

      /* Unicode-6.0 additions */
      {LTR, NONE, S, FALSE},    /* Batk */
      {LTR, NONE, S, FALSE},    /* Brah */
      {RTL, NONE, S, FALSE},    /* Mand */

      /* Unicode-6.1 additions */
      {LTR, NONE, S, FALSE},    /* Cakm */
      {RTL, NONE, S, FALSE},    /* Merc */
      {RTL, NONE, S, FALSE},    /* Mero */
      {LTR, NONE, S, FALSE},    /* Plrd */
      {LTR, NONE, S, FALSE},    /* Shrd */
      {LTR, NONE, S, FALSE},    /* Sora */
      {LTR, NONE, S, FALSE},    /* Takr */

      /* Unicode-7.0 additions */
      {LTR, NONE, S, FALSE},    /* Bass */
      {LTR, NONE, S, FALSE},    /* Aghb */
      {LTR, NONE, S, FALSE},    /* Dupl */
      {LTR, NONE, S, FALSE},    /* Elba */
      {LTR, NONE, S, FALSE},    /* Gran */
      {LTR, NONE, S, FALSE},    /* Khoj */
      {LTR, NONE, S, FALSE},    /* Sind */
      {LTR, NONE, S, FALSE},    /* Lina */
      {LTR, NONE, S, FALSE},    /* Mahj */
      {RTL, NONE, S, FALSE},    /* Mani */
      {RTL, NONE, S, FALSE},    /* Mend */
      {LTR, NONE, S, FALSE},    /* Modi */
      {LTR, NONE, S, FALSE},    /* Mroo */
      {RTL, NONE, S, FALSE},    /* Nbat */
      {RTL, NONE, S, FALSE},    /* Narb */
      {LTR, NONE, S, FALSE},    /* Perm */
      {LTR, NONE, S, FALSE},    /* Hmng */
      {RTL, NONE, S, FALSE},    /* Palm */
      {LTR, NONE, S, FALSE},    /* Pauc */
      {RTL, NONE, S, FALSE},    /* Phlp */
      {LTR, NONE, S, FALSE},    /* Sidd */
      {LTR, NONE, S, FALSE},    /* Tirh */
      {LTR, NONE, S, FALSE},    /* Wara */

      /* Unicode-8.0 additions */
      {LTR, NONE, S, FALSE},    /* Ahom */
      {LTR, NONE, S, FALSE},    /* Hluw */
      {RTL, NONE, S, FALSE},    /* Hatr */
      {LTR, NONE, S, FALSE},    /* Mult */
      {LTR, NONE, S, FALSE},    /* Hung */
      {LTR, NONE, S, FALSE},    /* Sgnw */

      /* Unicode-9.0 additions */
      {RTL, NONE, S, FALSE},    /* Adlm */
      {LTR, NONE, S, FALSE},    /* Bhks */
      {LTR, NONE, S, FALSE},    /* Marc */
      {LTR, NONE, S, FALSE},    /* Newa */
      {LTR, NONE, S, FALSE},    /* Osge */
      {LTR, NONE, S, FALSE},    /* Tang */

      /* Unicode-10.0 additions */
      {LTR, NONE, S, FALSE},    /* Gonm */
      {LTR, NONE, S, FALSE},    /* Nshu */
      {LTR, NONE, S, FALSE},    /* Soyo */
      {LTR, NONE, S, FALSE},    /* Zanb */

      /* Unicode-11.0 additions */
      {LTR, NONE, S, FALSE},    /* Dogr */
      {LTR, NONE, S, FALSE},    /* Gong */
      {RTL, NONE, S, FALSE},    /* Rohg */
      {LTR, NONE, S, FALSE},    /* Maka */
      {LTR, NONE, S, FALSE},    /* Medf */
      {RTL, NONE, S, FALSE},    /* Sogo */
      {RTL, NONE, S, FALSE},    /* Sogd */

      /* Unicode-12.0 additions */
      {RTL, NONE, S, FALSE},    /* Elym */
      {LTR, NONE, S, FALSE},    /* Nand */
      {LTR, NONE, S, FALSE},    /* Rohg */
      {LTR, NONE, S, FALSE},    /* Wcho */

      /* Unicode-13.0 additions */
      {RTL, NONE, S, FALSE},    /* Chrs */
      {LTR, NONE, S, FALSE},    /* Diak */
      {LTR, NONE, S, FALSE},    /* Kits */
      {RTL, NONE, S, FALSE},    /* Yezi */

      {LTR, NONE, S, FALSE},    /* Cpmn */
      {RTL, NONE, S, FALSE},    /* Ougr */
      {LTR, NONE, S, FALSE},    /* Tnsa */
      {LTR, NONE, S, FALSE},    /* Toto */
      {LTR, NONE, S, FALSE},    /* Vith */
};

#undef NONE
#undef TTB
#undef BTT

#undef LTR
#undef RTL
#undef WEAK

#undef S
#undef E
#undef N
#undef W

static Pango2ScriptProperties
get_script_properties (GUnicodeScript script)
{
  g_return_val_if_fail (script >= 0, script_properties[0]);

  if ((guint)script >= G_N_ELEMENTS (script_properties))
    return script_properties[0];

  return script_properties[script];
}

/**
 * pango2_gravity_get_for_script:
 * @script: `Pango2Script` to query
 * @base_gravity: base gravity of the paragraph
 * @hint: orientation hint
 *
 * Returns the gravity to use in laying out a `Pango2Item`.
 *
 * The gravity is determined based on the script, base gravity, and hint.
 *
 * If @base_gravity is %PANGO2_GRAVITY_AUTO, it is first replaced with the
 * preferred gravity of @script.  To get the preferred gravity of a script,
 * pass %PANGO2_GRAVITY_AUTO and %PANGO2_GRAVITY_HINT_STRONG in.
 *
 * Return value: resolved gravity suitable to use for a run of text
 * with @script
 */
Pango2Gravity
pango2_gravity_get_for_script (GUnicodeScript      script,
                              Pango2Gravity     base_gravity,
                              Pango2GravityHint hint)
{
  Pango2ScriptProperties props = get_script_properties (script);

  return pango2_gravity_get_for_script_and_width (script, props.wide,
                                                 base_gravity, hint);
}

/**
 * pango2_gravity_get_for_script_and_width:
 * @script: `Pango2Script` to query
 * @wide: %TRUE for wide characters as returned by g_unichar_iswide()
 * @base_gravity: base gravity of the paragraph
 * @hint: orientation hint
 *
 * Returns the gravity to use in laying out a single character
 * or `Pango2Item`.
 *
 * The gravity is determined based on the script, East Asian width,
 * base gravity, and hint,
 *
 * This function is similar to [func@Pango2.Gravity.get_for_script] except
 * that this function makes a distinction between narrow/half-width and
 * wide/full-width characters also. Wide/full-width characters always
 * stand *upright*, that is, they always take the base gravity,
 * whereas narrow/full-width characters are always rotated in vertical
 * context.
 *
 * If @base_gravity is %PANGO2_GRAVITY_AUTO, it is first replaced with the
 * preferred gravity of @script.
 *
 * Return value: resolved gravity suitable to use for a run of text
 * with @script and @wide.
 */
Pango2Gravity
pango2_gravity_get_for_script_and_width (GUnicodeScript    script,
                                         gboolean          wide,
                                         Pango2Gravity     base_gravity,
                                         Pango2GravityHint hint)
{
  Pango2ScriptProperties props = get_script_properties (script);
  gboolean vertical;

  if (G_UNLIKELY (base_gravity == PANGO2_GRAVITY_AUTO))
    base_gravity = props.preferred_gravity;

  vertical = PANGO2_GRAVITY_IS_VERTICAL (base_gravity);

  /* Everything is designed such that a system with no vertical support
   * renders everything correctly horizontally.  So, if not in a vertical
   * gravity, base and resolved gravities are always the same.
   *
   * Wide characters are always upright.
   */
  if (G_LIKELY (!vertical || wide))
    return base_gravity;

  /* If here, we have a narrow character in a vertical gravity setting.
   * Resolve depending on the hint.
   */
  switch (hint)
    {
    default:
    case PANGO2_GRAVITY_HINT_NATURAL:
      if (props.vert_dir == PANGO2_VERTICAL_DIRECTION_NONE)
        return PANGO2_GRAVITY_SOUTH;
      if ((base_gravity   == PANGO2_GRAVITY_EAST) ^
          (props.vert_dir == PANGO2_VERTICAL_DIRECTION_BTT))
        return PANGO2_GRAVITY_SOUTH;
      else
        return PANGO2_GRAVITY_NORTH;

    case PANGO2_GRAVITY_HINT_STRONG:
      return base_gravity;

    case PANGO2_GRAVITY_HINT_LINE:
      if ((base_gravity    == PANGO2_GRAVITY_EAST) ^
          (props.horiz_dir == PANGO2_DIRECTION_RTL))
        return PANGO2_GRAVITY_SOUTH;
      else
        return PANGO2_GRAVITY_NORTH;
    }
}
