//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2011 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================

#include "property.h"
#include "mscore.h"
#include "layoutbreak.h"
#include "groups.h"
#include "xml.h"
#include "note.h"
#include "barline.h"

namespace Ms {

//---------------------------------------------------------
//   PropertyData
//---------------------------------------------------------

struct PropertyData {
      P_ID id;
      bool link;              // link this property for linked elements
      const char* name;       // xml name of property
      P_TYPE type;
      };

//
// always: propertyList[subtype].id == subtype
//
//
static const PropertyData propertyList[] = {
      { P_ID::SUBTYPE,             false, "subtype",       P_TYPE::INT   },
      { P_ID::SELECTED,            false, "selected",      P_TYPE::BOOL  },
      { P_ID::GENERATED,           false, "generated",     P_TYPE::BOOL  },
      { P_ID::COLOR,               false, "color",         P_TYPE::COLOR },
      { P_ID::VISIBLE,             false, "visible",       P_TYPE::BOOL  },
      { P_ID::SMALL,               false, "small",         P_TYPE::BOOL  },
      { P_ID::SHOW_COURTESY,       false, "showCourtesy",  P_TYPE::INT   },
      { P_ID::LINE_TYPE,           false, "lineType",      P_TYPE::INT   },
      { P_ID::PITCH,               true,  "pitch",         P_TYPE::INT   },
      { P_ID::TPC1,                true,  "tpc",           P_TYPE::INT   },
      { P_ID::TPC2,                true,  "tpc2",          P_TYPE::INT   },

      { P_ID::LINE,                false, "line",          P_TYPE::INT   },
      { P_ID::FIXED,               false, "fixed",         P_TYPE::BOOL  },
      { P_ID::FIXED_LINE,          false, "fixedLine",     P_TYPE::INT   },
      { P_ID::HEAD_TYPE,           false, "headType",      P_TYPE::INT   },
      { P_ID::HEAD_GROUP,          false, "head",          P_TYPE::INT },
      { P_ID::VELO_TYPE,           false, "veloType",      P_TYPE::VALUE_TYPE },
      { P_ID::VELO_OFFSET,         false, "velocity",      P_TYPE::INT },
      { P_ID::ARTICULATION_ANCHOR, false, "anchor",        P_TYPE::INT },
      { P_ID::DIRECTION,           false, "direction",     P_TYPE::DIRECTION },
      { P_ID::STEM_DIRECTION,      false, "StemDirection", P_TYPE::DIRECTION },

      { P_ID::NO_STEM,             false, "noStem",        P_TYPE::INT },
      { P_ID::SLUR_DIRECTION,      false, "slurDirection", P_TYPE::INT },
      { P_ID::LEADING_SPACE,       false, "leadingSpace",  P_TYPE::SPATIUM },
      { P_ID::TRAILING_SPACE,      false, "trailingSpace", P_TYPE::SPATIUM },
      { P_ID::DISTRIBUTE,          false, "distribute",    P_TYPE::BOOL },
      { P_ID::MIRROR_HEAD,         false, "mirror",        P_TYPE::DIRECTION_H },
      { P_ID::DOT_POSITION,        false, "dotPosition",   P_TYPE::DIRECTION },
      { P_ID::TUNING,              false, "tuning",        P_TYPE::REAL  },
      { P_ID::PAUSE,               true,  "pause",         P_TYPE::REAL  },
      { P_ID::BARLINE_SPAN,        false, "barlineSpan",   P_TYPE::INT   },

      { P_ID::BARLINE_SPAN_FROM,   false, 0,               P_TYPE::INT   },
      { P_ID::BARLINE_SPAN_TO,     false, 0,               P_TYPE::INT   },
      { P_ID::USER_OFF,            false, "userOff",       P_TYPE::POINT },
      { P_ID::FRET,                true,  "fret",          P_TYPE::INT   },
      { P_ID::STRING,              true,  "string",        P_TYPE::INT   },
      { P_ID::GHOST,               true,  "ghost",         P_TYPE::BOOL  },
      { P_ID::PLAY,                false, "play",          P_TYPE::BOOL  },
      { P_ID::TIMESIG_NOMINAL,     false, 0,               P_TYPE::FRACTION },
      { P_ID::TIMESIG_ACTUAL,      true,  0,               P_TYPE::FRACTION },
      { P_ID::NUMBER_TYPE,         false, "numberType",    P_TYPE::INT   },

      { P_ID::BRACKET_TYPE,        false, "bracketType",   P_TYPE::INT   },
      { P_ID::NORMAL_NOTES,        false, "normalNotes",   P_TYPE::INT   },
      { P_ID::ACTUAL_NOTES,        false, "actualNotes",   P_TYPE::INT   },
      { P_ID::P1,                  false, "p1",            P_TYPE::POINT },
      { P_ID::P2,                  false, "p2",            P_TYPE::POINT },
      { P_ID::GROW_LEFT,           false, "growLeft",      P_TYPE::REAL    },
      { P_ID::GROW_RIGHT,          false, "growRight",     P_TYPE::REAL    },
      { P_ID::BOX_HEIGHT,          false, "height",        P_TYPE::SPATIUM },
      { P_ID::BOX_WIDTH,           false, "width",         P_TYPE::SPATIUM },
      { P_ID::TOP_GAP,             false, "topGap",        P_TYPE::SP_REAL },

      { P_ID::BOTTOM_GAP,          false, "bottomGap",     P_TYPE::SP_REAL },
      { P_ID::LEFT_MARGIN,         false, "leftMargin",    P_TYPE::REAL    },
      { P_ID::RIGHT_MARGIN,        false, "rightMargin",   P_TYPE::REAL    },
      { P_ID::TOP_MARGIN,          false, "topMargin",     P_TYPE::REAL    },
      { P_ID::BOTTOM_MARGIN,       false, "bottomMargin",  P_TYPE::REAL    },
      { P_ID::LAYOUT_BREAK,        false, "subtype",       P_TYPE::LAYOUT_BREAK },
      { P_ID::AUTOSCALE,           false, "autoScale",     P_TYPE::BOOL   },
      { P_ID::SIZE,                false, "size",            P_TYPE::SIZE },
      { P_ID::SCALE,               false, 0,                 P_TYPE::SCALE  },
      { P_ID::LOCK_ASPECT_RATIO,   false, "lockAspectRatio", P_TYPE::BOOL },

      { P_ID::SIZE_IS_SPATIUM,     false, "sizeIsSpatium",   P_TYPE::BOOL },
      { P_ID::TEXT_STYLE,          false, "textStyle",       P_TYPE::TEXT_STYLE  },
      { P_ID::TEXT_STYLE_TYPE,     false, "textStyleType", P_TYPE::INT  },
      { P_ID::TEXT,                false, 0,               P_TYPE::STRING },
      { P_ID::HTML_TEXT,           false, 0,               P_TYPE::STRING },
      { P_ID::USER_MODIFIED,       false, 0,               P_TYPE::BOOL   },
      { P_ID::BEAM_POS,            false, 0,               P_TYPE::POINT  },
      { P_ID::BEAM_MODE,           true, "BeamMode",       P_TYPE::BEAM_MODE  },
      { P_ID::BEAM_NO_SLOPE,       true, "noSlope",        P_TYPE::BOOL   },
      { P_ID::USER_LEN,            false, "userLen",       P_TYPE::REAL   },

      { P_ID::SPACE,               false, "space",         P_TYPE::SP_REAL},
      { P_ID::TEMPO,               true,  "tempo",         P_TYPE::TEMPO  },
      { P_ID::TEMPO_FOLLOW_TEXT,   true,  "followText",    P_TYPE::BOOL   },
      { P_ID::ACCIDENTAL_BRACKET,  false, "bracket",       P_TYPE::BOOL   },
      { P_ID::NUMERATOR_STRING,    false, "textN",         P_TYPE::STRING },
      { P_ID::DENOMINATOR_STRING,  false, "textD",         P_TYPE::STRING },
      { P_ID::BREAK_HINT,          false, "breakHint",     P_TYPE::BOOL   },
      { P_ID::FBPREFIX,            false, "prefix",        P_TYPE::INT    },
      { P_ID::FBDIGIT,             false, "digit",         P_TYPE::INT    },
      { P_ID::FBSUFFIX,            false, "suffix",        P_TYPE::INT    },

      { P_ID::FBCONTINUATIONLINE,  false, "continuationLine", P_TYPE::INT },
      { P_ID::FBPARENTHESIS1,      false, "",              P_TYPE::INT    },
      { P_ID::FBPARENTHESIS2,      false, "",              P_TYPE::INT    },
      { P_ID::FBPARENTHESIS3,      false, "",              P_TYPE::INT    },
      { P_ID::FBPARENTHESIS4,      false, "",              P_TYPE::INT    },
      { P_ID::FBPARENTHESIS5,      false, "",              P_TYPE::INT    },
      { P_ID::VOLTA_TYPE,          false, "",              P_TYPE::INT    },
      { P_ID::OTTAVA_TYPE,         false, "",              P_TYPE::INT    },
      { P_ID::NUMBERS_ONLY,        false, "numbersOnly",   P_TYPE::BOOL   },
      { P_ID::TRILL_TYPE,          false, "",              P_TYPE::INT    },

      { P_ID::HAIRPIN_TEXTLINE,    false, "useTextLine",   P_TYPE::BOOL   },
      { P_ID::HAIRPIN_CIRCLEDTIP,  false, "hairpinCircledTip", P_TYPE::BOOL     },
      { P_ID::HAIRPIN_TYPE,        true,  "",              P_TYPE::INT     },
      { P_ID::HAIRPIN_HEIGHT,      false, "hairpinHeight",     P_TYPE::SPATIUM },
      { P_ID::HAIRPIN_CONT_HEIGHT, false, "hairpinContHeight", P_TYPE::SPATIUM },
      { P_ID::VELO_CHANGE,         true,  "veloChange",        P_TYPE::INT    },
      { P_ID::DYNAMIC_RANGE,       true,  "dynType",       P_TYPE::INT    },
      { P_ID::PLACEMENT,           false, "placement",     P_TYPE::PLACEMENT    },
      { P_ID::VELOCITY,            false, "velocity",      P_TYPE::INT    },
      { P_ID::JUMP_TO,             false, "jumpTo",        P_TYPE::STRING },
      { P_ID::PLAY_UNTIL,          false, "playUntil",     P_TYPE::STRING },

      { P_ID::CONTINUE_AT,         false, "continueAt",    P_TYPE::STRING },
      { P_ID::LABEL,               false, "label",         P_TYPE::STRING },
      { P_ID::MARKER_TYPE,         false, 0,               P_TYPE::INT    },
      { P_ID::ARP_USER_LEN1,       false, 0,               P_TYPE::REAL   },
      { P_ID::ARP_USER_LEN2,       false, 0,               P_TYPE::REAL   },
      { P_ID::REPEAT_FLAGS,        false,  0,              P_TYPE::INT    },
      { P_ID::END_BARLINE_TYPE,    false, 0,               P_TYPE::INT    },
      { P_ID::END_BARLINE_VISIBLE, false, 0,               P_TYPE::BOOL   },
      { P_ID::END_BARLINE_COLOR,   false, 0,               P_TYPE::COLOR  },
      { P_ID::MEASURE_NUMBER_MODE, false, "measureNumberMode", P_TYPE::INT    },

      { P_ID::GLISS_TYPE,          false, 0,               P_TYPE::INT    },
      { P_ID::GLISS_TEXT,          false, 0,               P_TYPE::STRING },
      { P_ID::GLISS_SHOW_TEXT,     false, 0,               P_TYPE::BOOL   },
      { P_ID::DIAGONAL,            false, 0,               P_TYPE::BOOL     },
      { P_ID::GROUPS,              false, 0,               P_TYPE::GROUPS   },
      { P_ID::LINE_STYLE,          false, "lineStyle",     P_TYPE::INT      },
      { P_ID::LINE_COLOR,          false, 0,               P_TYPE::COLOR    },
      { P_ID::LINE_WIDTH,          false, 0,               P_TYPE::SPATIUM  },
      { P_ID::LASSO_POS,           false, 0,               P_TYPE::POINT_MM },
      { P_ID::LASSO_SIZE,          false, 0,               P_TYPE::SIZE_MM  },

      { P_ID::TIME_STRETCH,        false, 0,               P_TYPE::REAL     },
      { P_ID::ORNAMENT_STYLE,      false, "ornamentStyle", P_TYPE::ORNAMENT_STYLE},

      { P_ID::TIMESIG,             false, 0,               P_TYPE::FRACTION },
      { P_ID::TIMESIG_GLOBAL,      false, 0,               P_TYPE::FRACTION },
      { P_ID::TIMESIG_STRETCH,     false, 0,               P_TYPE::FRACTION },
      { P_ID::TIMESIG_TYPE,        true,  0,               P_TYPE::INT },
      { P_ID::SPANNER_TICK,        true,  "tick",          P_TYPE::INT      },
      { P_ID::SPANNER_TICKS,       true,  "ticks",           P_TYPE::INT      },
      { P_ID::SPANNER_TRACK2,      true,  "track2",          P_TYPE::INT      },
      { P_ID::USER_OFF2,           false, "userOff2",        P_TYPE::POINT   },
      { P_ID::BEGIN_TEXT_PLACE,    false, "beginTextPlace",  P_TYPE::INT     },
      { P_ID::CONTINUE_TEXT_PLACE, false, "continueTextPlace",  P_TYPE::INT     },

      { P_ID::END_TEXT_PLACE,      false, "endTextPlace",    P_TYPE::INT     },
      { P_ID::BEGIN_HOOK,          false, "beginHook",       P_TYPE::BOOL    },
      { P_ID::END_HOOK,            false, "endHook",         P_TYPE::BOOL    },
      { P_ID::BEGIN_HOOK_HEIGHT,   false, "beginHookHeight", P_TYPE::SPATIUM },
      { P_ID::END_HOOK_HEIGHT,     false, "endHookHeight",   P_TYPE::SPATIUM },
      { P_ID::BEGIN_HOOK_TYPE,     false, "beginHookType",   P_TYPE::INT     },
      { P_ID::END_HOOK_TYPE,       false, "endHookType",     P_TYPE::INT     },
      { P_ID::BEGIN_TEXT,          true,  "beginText",       P_TYPE::STRING  },
      { P_ID::CONTINUE_TEXT,       true,  "continueText",    P_TYPE::STRING  },
      { P_ID::END_TEXT,            true,  "endText",         P_TYPE::STRING  },

      { P_ID::BEGIN_TEXT_STYLE,    false, "beginTextStyle",    P_TYPE::TEXT_STYLE },
      { P_ID::CONTINUE_TEXT_STYLE, false, "continueTextStyle", P_TYPE::TEXT_STYLE },
      { P_ID::END_TEXT_STYLE,      false, "endTextStyle",      P_TYPE::TEXT_STYLE },
      { P_ID::BREAK_MMR,           false, "breakMultiMeasureRest", P_TYPE::BOOL },
      { P_ID::REPEAT_COUNT,        true,  "endRepeat",             P_TYPE::INT  },
      { P_ID::USER_STRETCH,        false, "stretch",               P_TYPE::REAL },
      { P_ID::NO_OFFSET,           false, "noOffset",              P_TYPE::INT  },
      { P_ID::IRREGULAR,           true,  "irregular",             P_TYPE::BOOL },
      { P_ID::ANCHOR,              false,  "anchor",               P_TYPE::INT },

      { P_ID::SLUR_UOFF1,          false,  "o1",                   P_TYPE::POINT  },
      { P_ID::SLUR_UOFF2,          false,  "o2",                   P_TYPE::POINT  },
      { P_ID::SLUR_UOFF3,          false,  "o3",                   P_TYPE::POINT  },
      { P_ID::SLUR_UOFF4,          false,  "o4",                   P_TYPE::POINT  },
      { P_ID::STAFF_MOVE,          true,  "move",                  P_TYPE::INT },
      { P_ID::SYLLABIC,            true,  "syllabic",              P_TYPE::INT },
      { P_ID::LYRIC_TICKS,         true,  "ticks",                 P_TYPE::INT },
      { P_ID::VOLTA_ENDING,        true,  "endings",               P_TYPE::INT_LIST },
      { P_ID::LINE_VISIBLE,        true,  "lineVisible",           P_TYPE::BOOL },

      { P_ID::SYSTEM_INITIAL_BARLINE_TYPE, false, "sysInitBarLineType", P_TYPE::BARLINE_TYPE },
      { P_ID::MAG,                 false, "mag",                   P_TYPE::REAL },
      { P_ID::USE_DRUMSET,         false, "useDrumset",            P_TYPE::BOOL },
      { P_ID::PART_VOLUME,         false, "volume",                P_TYPE::INT },
      { P_ID::PART_MUTE,           false, "mute",                  P_TYPE::BOOL },
      { P_ID::PART_PAN,            false, "pan",                   P_TYPE::INT },
      { P_ID::PART_REVERB,         false, "reverb",                P_TYPE::INT },
      { P_ID::PART_CHORUS,         false, "chorus",                P_TYPE::INT },

      { P_ID::DURATION,            false, 0,                       P_TYPE::FRACTION },
      { P_ID::DURATION_TYPE,       false, 0,                       P_TYPE::TDURATION },
      { P_ID::ROLE,                false, "role",                  P_TYPE::INT },
      { P_ID::TRACK,               false, 0,                       P_TYPE::INT },

      { P_ID::GLISSANDO_STYLE,     false, "glissandoStyle",        P_TYPE::GLISSANDO_STYLE},

      { P_ID::LAYOUT_MODE,         false, 0,                       P_TYPE::INT },

      { P_ID::FRET_STRINGS,        false, "strings",               P_TYPE::INT },
      { P_ID::FRET_FRETS,          false, "frets",                 P_TYPE::INT },
      { P_ID::FRET_BARRE,          false, "barre",                 P_TYPE::INT },
      { P_ID::FRET_OFFSET,         false, "fretOffset",            P_TYPE::INT },
      { P_ID::PLAY_REPEATS,        false, "playRepeats",           P_TYPE::BOOL},

      { P_ID::END,                 false, "",                      P_TYPE::INT }
      };

//---------------------------------------------------------
//   propertyType
//---------------------------------------------------------

P_TYPE propertyType(P_ID id)
      {
          Q_ASSERT( propertyList[int(id)].id == id);

          return propertyList[int(id)].type;
      }

//---------------------------------------------------------
//   propertyLink
//---------------------------------------------------------

bool propertyLink(P_ID id)
      {
          Q_ASSERT( propertyList[int(id)].id == id);
          return propertyList[int(id)].link;
      }

//---------------------------------------------------------
//   propertyName
//---------------------------------------------------------

const char* propertyName(P_ID id)
      {
          Q_ASSERT( propertyList[int(id)].id == id);
          return propertyList[int(id)].name;
      }

//---------------------------------------------------------
//    getProperty
//---------------------------------------------------------

QVariant getProperty(P_ID id, XmlReader& e)
      {
      switch (propertyType(id)) {
            case P_TYPE::BOOL:
                  return QVariant(bool(e.readInt()));
            case P_TYPE::SUBTYPE:
            case P_TYPE::INT:
                  return QVariant(e.readInt());
            case P_TYPE::REAL:
            case P_TYPE::SPATIUM:
            case P_TYPE::SP_REAL:
            case P_TYPE::TEMPO:
                  return QVariant(e.readDouble());
            case P_TYPE::FRACTION:
                  return QVariant::fromValue(e.readFraction());
            case P_TYPE::COLOR:
                  return QVariant(e.readColor());
            case P_TYPE::POINT:
                  return QVariant(e.readPoint());
            case P_TYPE::SCALE:
            case P_TYPE::SIZE:
                  return QVariant(e.readSize());
            case P_TYPE::STRING:
                  return QVariant(e.readElementText());
            case P_TYPE::GLISSANDO_STYLE: {
                QString value(e.readElementText());
                if ( value == "whitekeys")
                    return QVariant(int(MScore::GlissandoStyle::WHITE_KEYS));
                else if ( value == "blackkeys")
                    return QVariant(int(MScore::GlissandoStyle::BLACK_KEYS));
                else if ( value == "diatonic")
                    return QVariant(int(MScore::GlissandoStyle::DIATONIC));
                else // e.g., normally "Chromatic"
                    return QVariant(int(MScore::GlissandoStyle::CHROMATIC));
            }
              break;
            case P_TYPE::ORNAMENT_STYLE:
                  {
                      QString value(e.readElementText());
                      if ( value == "baroque")
                          return QVariant(int(MScore::OrnamentStyle::BAROQUE));

                      return QVariant(int(MScore::OrnamentStyle::DEFAULT));
                  }
                  break; // break is really not necessary because of the default return
            case P_TYPE::DIRECTION:
                  {
                  QString value(e.readElementText());
                  if (value == "up")
                        return QVariant(int(MScore::Direction::UP));
                  else if (value == "down")
                        return QVariant(int(MScore::Direction::DOWN));
                  else if (value == "auto")
                        return QVariant(int(MScore::Direction::AUTO));
                  }
                  break;
            case P_TYPE::DIRECTION_H:
                  {
                  QString value(e.readElementText());
                  if (value == "left" || value == "1")
                        return QVariant(int(MScore::DirectionH::LEFT));
                  else if (value == "right" || value == "2")
                        return QVariant(int(MScore::DirectionH::RIGHT));
                  else if (value == "auto")
                        return QVariant(int(MScore::DirectionH::AUTO));
                  }
                  break;
            case P_TYPE::LAYOUT_BREAK: {
                  QString value(e.readElementText());
                  if (value == "line")
                        return QVariant(int(LayoutBreak::Type::LINE));
                  if (value == "page")
                        return QVariant(int(LayoutBreak::Type::PAGE));
                  if (value == "section")
                        return QVariant(int(LayoutBreak::Type::SECTION));
                  qDebug("getProperty: invalid P_TYPE::LAYOUT_BREAK: <%s>", qPrintable(value));
                  }
                  break;
            case P_TYPE::VALUE_TYPE: {
                  QString value(e.readElementText());
                  if (value == "offset")
                        return QVariant(int(Note::ValueType::OFFSET_VAL));
                  else if (value == "user")
                        return QVariant(int(Note::ValueType::USER_VAL));
                  }
                  break;
            case P_TYPE::PLACEMENT: {
                  QString value(e.readElementText());
                  if (value == "above")
                        return QVariant(int(Element::Placement::ABOVE));
                  else if (value == "below")
                        return QVariant(int(Element::Placement::BELOW));
                  }
                  break;
            case P_TYPE::BARLINE_TYPE: {
                  bool ok;
                  const QString& val(e.readElementText());
                  // In MuseScore 2.0.2 and before, SYSTEM_INITIAL_BARLINE_TYPE
                  // was stored as a int, so try this first
                  int ct = val.toInt(&ok);
                  if (ok)
                        return QVariant(ct);
                  else {
                        for (unsigned i = 0; i < BarLine::barLineTableSize(); ++i) {
                              if (BarLine::barLineTypeName(BarLineType(i)) == val) {
                                    ct = i;
                                    break;
                                    }
                              }
                        return QVariant(ct);
                        }
                  }
                  break;
            case P_TYPE::BEAM_MODE:             // TODO
                  return QVariant(int(0));

            case P_TYPE::GROUPS:
                  {
                  Groups g;
                  g.read(e);
                  return QVariant::fromValue(g);
                  }
            case P_TYPE::POINT_MM:              // not supported
            case P_TYPE::TDURATION:
            case P_TYPE::SIZE_MM:
            case P_TYPE::SYMID:
            case P_TYPE::TEXT_STYLE:
            case P_TYPE::INT_LIST:
                  return QVariant();
            }
      return QVariant();
      }

}

