//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2017 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
//=============================================================================

#ifndef __SCORE_ELEMENT_H__
#define __SCORE_ELEMENT_H__

#include "types.h"
#include "style.h"

namespace Ms {

class ScoreElement;
class MasterScore;
class XmlWriter;
class ConnectorInfoReader;
class Measure;
class Staff;
class Part;
class Score;
class Sym;
class MuseScoreView;
class Segment;
class Element;
class BarLine;
class Articulation;
class Marker;
class Clef;
class KeySig;
class TimeSig;
class TempoText;
class Breath;
class Box;
class HBox;
class VBox;
class TBox;
class FBox;
class ChordRest;
class Slur;
class Tie;
class Glissando;
class GlissandoSegment;
class SystemDivider;
class RehearsalMark;
class Harmony;
class Volta;
class Jump;
class StaffText;
class Ottava;
class Note;
class Chord;
class Rest;
class LayoutBreak;
class Tremolo;
class System;
class Sticking;
class Lyrics;
class LyricsLine;
class LyricsLineSegment;
class Stem;
class SlurSegment;
class TieSegment;
class OttavaSegment;
class Beam;
class Hook;
class StemSlash;
class Spacer;
class StaffLines;
class Ambitus;
class Bracket;
class InstrumentChange;
class Text;
class TextBase;
class Hairpin;
class HairpinSegment;
class Bend;
class TremoloBar;
class RepeatMeasure;
class Tuplet;
class NoteDot;
class Dynamic;
class InstrumentName;
class DurationElement;
class Accidental;
class TextLine;
class TextLineSegment;
class Pedal;
class PedalSegment;
class LedgerLine;
class Icon;
class VoltaSegment;
class NoteLine;
class Trill;
class TrillSegment;
class Symbol;
class FSymbol;
class Fingering;
class NoteHead;
class FiguredBass;
class StaffState;
class Arpeggio;
class Image;
class ChordLine;
class SlurTieSegment;
class FretDiagram;
class StaffTypeChange;
class MeasureBase;
class Page;
class SystemText;
class BracketItem;
class Spanner;
class SpannerSegment;
class BagpipeEmbellishment;
class LineSegment;
class BSymbol;
class TextLineBase;
class Fermata;

class LetRing;
class LetRingSegment;
class Vibrato;
class VibratoSegment;
class PalmMute;
class PalmMuteSegment;
class MeasureNumber;

class StaffTextBase;

enum class Pid : int;
enum class PropertyFlags : char;
enum class Sid : int;

//---------------------------------------------------------
//   LinkedElements
//---------------------------------------------------------

class LinkedElements : public QList<ScoreElement*> {
      int _lid;         // unique id for every linked list

   public:
      LinkedElements(Score*);
      LinkedElements(Score*, int id);

      void setLid(Score*, int val);
      int lid() const   { return _lid;    }

      ScoreElement* mainElement();
      };

//---------------------------------------------------------
//   ElementName
//---------------------------------------------------------

struct ElementName {
      ElementType type;
      const char* name;
      const char* userName;
      };

//---------------------------------------------------------
//   ScoreElement
//---------------------------------------------------------

class ScoreElement {
      Score* _score;
      static ElementStyle const emptyStyle;

   protected:
      const ElementStyle* _elementStyle { &emptyStyle };
      PropertyFlags* _propertyFlagsList { 0 };
      LinkedElements* _links            { 0 };
      virtual int getPropertyFlagsIdx(Pid id) const;

   public:
      ScoreElement(Score* s) : _score(s)   {}
      ScoreElement(const ScoreElement& se);

      virtual ~ScoreElement();

      Score* score() const                 { return _score;      }
      MasterScore* masterScore() const;
      virtual void setScore(Score* s)      { _score = s;         }
      const char* name() const;
      virtual QString userName() const;
      virtual ElementType type() const = 0;

      static ElementType name2type(const QStringRef&, bool silent = false);
      static ElementType name2type(const QString& s) { return name2type(QStringRef(&s)); }
      static const char* name(ElementType);

      virtual QVariant getProperty(Pid) const = 0;
      virtual bool setProperty(Pid, const QVariant&) = 0;
      virtual QVariant propertyDefault(Pid) const;
      virtual void resetProperty(Pid id);
      QVariant propertyDefault(Pid pid, Tid tid) const;
      virtual bool sizeIsSpatiumDependent() const { return true; }

      virtual void reset();                     // reset all properties & position to default

      virtual Pid propertyId(const QStringRef& xmlName) const;
      virtual QString propertyUserValue(Pid) const;

      virtual void initElementStyle(const ElementStyle*);
      virtual const ElementStyle* styledProperties() const   { return _elementStyle; }

      virtual PropertyFlags* propertyFlagsList() const   { return _propertyFlagsList; }
      virtual PropertyFlags propertyFlags(Pid) const;
      bool isStyled(Pid pid) const;
      QVariant styleValue(Pid, Sid) const;

      virtual void setPropertyFlags(Pid, PropertyFlags);

      virtual Sid getPropertyStyle(Pid) const;
      bool readProperty(const QStringRef&, XmlReader&, Pid);
      void readProperty(XmlReader&, Pid);
      bool readStyledProperty(XmlReader& e, const QStringRef& tag);

      virtual void readAddConnector(ConnectorInfoReader* info, bool pasteMode);

      virtual void styleChanged();

      virtual void undoChangeProperty(Pid id, const QVariant&, PropertyFlags ps);
      void undoChangeProperty(Pid id, const QVariant&);
      void undoResetProperty(Pid id);

      void undoPushProperty(Pid);
      void writeProperty(XmlWriter& xml, Pid id) const;
      void writeStyledProperties(XmlWriter&) const;

      QList<ScoreElement*> linkList() const;

      void linkTo(ScoreElement*);
      void unlink();
      bool isLinked(ScoreElement*);

      virtual void undoUnlink();
      int lid() const                         { return _links ? _links->lid() : 0; }
      LinkedElements* links() const           { return _links;      }
      void setLinks(LinkedElements* le)       { _links = le;        }

      //---------------------------------------------------
      // check type
      //
      // Example for ChordRest:
      //
      //    bool             isChordRest()
      //---------------------------------------------------


#define CONVERT(a,b) \
      bool is##a() const { return type() == ElementType::b; }

      CONVERT(Note,          NOTE)
      CONVERT(Rest,          REST)
      CONVERT(Chord,         CHORD)
      CONVERT(BarLine,       BAR_LINE)
      CONVERT(Articulation,  ARTICULATION)
      CONVERT(Fermata,       FERMATA)
      CONVERT(Marker,        MARKER)
      CONVERT(Clef,          CLEF)
      CONVERT(KeySig,        KEYSIG)
      CONVERT(TimeSig,       TIMESIG)
      CONVERT(Measure,       MEASURE)
      CONVERT(TempoText,     TEMPO_TEXT)
      CONVERT(Breath,        BREATH)
      CONVERT(HBox,          HBOX)
      CONVERT(VBox,          VBOX)
      CONVERT(TBox,          TBOX)
      CONVERT(FBox,          FBOX)
      CONVERT(Tie,           TIE)
      CONVERT(Slur,          SLUR)
      CONVERT(Glissando,     GLISSANDO)
      CONVERT(GlissandoSegment,     GLISSANDO_SEGMENT)
      CONVERT(SystemDivider, SYSTEM_DIVIDER)
      CONVERT(RehearsalMark, REHEARSAL_MARK)
      CONVERT(Harmony,       HARMONY)
      CONVERT(Volta,         VOLTA)
      CONVERT(Jump,          JUMP)
      CONVERT(Ottava,        OTTAVA)
      CONVERT(LayoutBreak,   LAYOUT_BREAK)
      CONVERT(Segment,       SEGMENT)
      CONVERT(Tremolo,       TREMOLO)
      CONVERT(System,        SYSTEM)
      CONVERT(Lyrics,        LYRICS)
      CONVERT(Stem,          STEM)
      CONVERT(Beam,          BEAM)
      CONVERT(Hook,          HOOK)
      CONVERT(StemSlash,     STEM_SLASH)
      CONVERT(SlurSegment,   SLUR_SEGMENT)
      CONVERT(TieSegment,    TIE_SEGMENT)
      CONVERT(Spacer,        SPACER)
      CONVERT(StaffLines,    STAFF_LINES)
      CONVERT(Ambitus,       AMBITUS)
      CONVERT(Bracket,       BRACKET)
      CONVERT(InstrumentChange, INSTRUMENT_CHANGE)
      CONVERT(StaffTypeChange, STAFFTYPE_CHANGE)
      CONVERT(Hairpin,       HAIRPIN)
      CONVERT(HairpinSegment,HAIRPIN_SEGMENT)
      CONVERT(Bend,          BEND)
      CONVERT(TremoloBar,    TREMOLOBAR)
      CONVERT(RepeatMeasure, REPEAT_MEASURE)
      CONVERT(Tuplet,        TUPLET)
      CONVERT(NoteDot,       NOTEDOT)
      CONVERT(Dynamic,       DYNAMIC)
      CONVERT(InstrumentName, INSTRUMENT_NAME)
      CONVERT(Accidental,    ACCIDENTAL)
      CONVERT(TextLine,      TEXTLINE)
      CONVERT(TextLineSegment,      TEXTLINE_SEGMENT)
      CONVERT(Pedal,         PEDAL)
      CONVERT(PedalSegment,  PEDAL_SEGMENT)
      CONVERT(OttavaSegment, OTTAVA_SEGMENT)
      CONVERT(LedgerLine,    LEDGER_LINE)
      CONVERT(Icon,          ICON)
      CONVERT(VoltaSegment,  VOLTA_SEGMENT)
      CONVERT(NoteLine,      NOTELINE)
      CONVERT(Trill,         TRILL)
      CONVERT(TrillSegment,  TRILL_SEGMENT)
      CONVERT(LetRing,       LET_RING)
      CONVERT(LetRingSegment, LET_RING_SEGMENT)
      CONVERT(Vibrato,       VIBRATO)
      CONVERT(PalmMute,      PALM_MUTE)
      CONVERT(PalmMuteSegment, PALM_MUTE_SEGMENT)
      CONVERT(VibratoSegment,  VIBRATO_SEGMENT)
      CONVERT(Symbol,        SYMBOL)
      CONVERT(FSymbol,       FSYMBOL)
      CONVERT(Fingering,     FINGERING)
      CONVERT(NoteHead,      NOTEHEAD)
      CONVERT(LyricsLine,    LYRICSLINE)
      CONVERT(LyricsLineSegment, LYRICSLINE_SEGMENT)
      CONVERT(FiguredBass,   FIGURED_BASS)
      CONVERT(StaffState,    STAFF_STATE)
      CONVERT(Arpeggio,      ARPEGGIO)
      CONVERT(Image,         IMAGE)
      CONVERT(ChordLine,     CHORDLINE)
      CONVERT(FretDiagram,   FRET_DIAGRAM)
      CONVERT(Page,          PAGE)
      CONVERT(Text,          TEXT)
      CONVERT(MeasureNumber, MEASURE_NUMBER)
      CONVERT(StaffText,     STAFF_TEXT)
      CONVERT(SystemText,    SYSTEM_TEXT)
      CONVERT(BracketItem,   BRACKET_ITEM)
      CONVERT(Score,         SCORE)
      CONVERT(Staff,         STAFF)
      CONVERT(Part,          PART)
      CONVERT(BagpipeEmbellishment, BAGPIPE_EMBELLISHMENT)
      CONVERT(Sticking,      STICKING)
#undef CONVERT

      virtual bool isElement() const { return false; } // overriden in element.h
      bool isChordRest() const       { return isRest() || isChord() || isRepeatMeasure(); }
      bool isDurationElement() const { return isChordRest() || isTuplet(); }
      bool isSlurTieSegment() const  { return isSlurSegment() || isTieSegment(); }
      bool isSLineSegment() const;
      bool isBox() const { return isVBox() || isHBox() || isTBox() || isFBox(); }
      bool isVBoxBase() const { return isVBox() || isTBox() || isFBox(); }
      bool isMeasureBase() const { return isMeasure() || isBox(); }
      bool isTextBase() const;
      bool isTextLineBaseSegment() const {
         return isHairpinSegment()
         || isLetRingSegment()
         || isTextLineSegment()
         || isOttavaSegment()
         || isPalmMuteSegment()
         || isPedalSegment()
         || isVoltaSegment()
         ;
         }
      bool isLineSegment() const {
         return isGlissandoSegment()
         || isLyricsLineSegment()
         || isTextLineBaseSegment()
         || isTrillSegment()
         || isVibratoSegment()
         ;
         }
      bool isSpannerSegment() const { return isLineSegment() || isTextLineBaseSegment() || isSlurSegment() || isTieSegment(); }
      bool isBSymbol() const { return isImage() || isSymbol(); }
      bool isTextLineBase() const {
            return isHairpin()
            || isLetRing()
            || isNoteLine()
            || isOttava()
            || isPalmMute()
            || isPedal()
            || isTextLine()
            || isVolta()
            ;
            }
      bool isSLine() const {
            return isTextLineBase() || isTrill() || isGlissando() || isVibrato();
            }

      bool isSpanner() const {
         return isSlur()
         || isTie()
         || isGlissando()
         || isLyricsLine()
         || isTextLineBase()
         || isSLine()
         ;
         }
      bool isStaffTextBase() const {
            return isStaffText() || isSystemText();
            }
      };

//---------------------------------------------------
// safe casting of ScoreElement
//
// Example for ChordRest:
//
//    ChordRest* toChordRest(Element* e)
//---------------------------------------------------

static inline ChordRest* toChordRest(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
         || e->type() == ElementType::REPEAT_MEASURE);
      return (ChordRest*)e;
      }
static inline const ChordRest* toChordRest(const ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
         || e->type() == ElementType::REPEAT_MEASURE);
      return (const ChordRest*)e;
      }
static inline DurationElement* toDurationElement(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
         || e->type() == ElementType::REPEAT_MEASURE || e->type() == ElementType::TUPLET);
      return (DurationElement*)e;
      }
static inline const DurationElement* toDurationElement(const ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
         || e->type() == ElementType::REPEAT_MEASURE || e->type() == ElementType::TUPLET);
      return (const DurationElement*)e;
      }
static inline Rest* toRest(ScoreElement* e) {
      Q_ASSERT(!e || e->isRest() || e->isRepeatMeasure());
      return (Rest*)e;
      }
static inline const Rest* toRest(const ScoreElement* e) {
      Q_ASSERT(!e || e->isRest() || e->isRepeatMeasure());
      return (const Rest*)e;
      }
static inline SlurTieSegment* toSlurTieSegment(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT);
      return (SlurTieSegment*)e;
      }
static inline const SlurTieSegment* toSlurTieSegment(const ScoreElement* e) {
      Q_ASSERT(e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT);
      return (const SlurTieSegment*)e;
      }
static inline const MeasureBase* toMeasureBase(const ScoreElement* e) {
     Q_ASSERT(e == 0 || e->isMeasure() || e->isVBox() || e->isHBox() || e->isTBox() || e->isFBox());
      return (const MeasureBase*)e;
      }
static inline MeasureBase* toMeasureBase(ScoreElement* e) {
     Q_ASSERT(e == 0 || e->isMeasureBase());
      return (MeasureBase*)e;
      }
static inline Box* toBox(ScoreElement* e) {
     Q_ASSERT(e == 0 || e->isBox());
      return (Box*)e;
      }
static inline SpannerSegment* toSpannerSegment(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isSpannerSegment());
      return (SpannerSegment*)e;
      }
static inline BSymbol* toBSymbol(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isBSymbol());
      return (BSymbol*)e;
      }
static inline TextLineBase* toTextLineBase(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isTextLineBase());
      return (TextLineBase*)e;
      }
static inline TextBase* toTextBase(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isTextBase());
      return (TextBase*)e;
      }
static inline const TextBase* toTextBase(const ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isTextBase());
      return (const TextBase*)e;
      }
static inline StaffTextBase* toStaffTextBase(ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isStaffTextBase());
      return (StaffTextBase*)e;
      }
static inline const StaffTextBase* toStaffTextBase(const ScoreElement* e) {
      Q_ASSERT(e == 0 || e->isStaffTextBase());
      return (const StaffTextBase*)e;
      }

#define CONVERT(a)  \
static inline a* to##a(ScoreElement* e)             { Q_ASSERT(e == 0 || e->is##a()); return (a*)e; } \
static inline const a* to##a(const ScoreElement* e) { Q_ASSERT(e == 0 || e->is##a()); return (const a*)e; }

      CONVERT(Element)
      CONVERT(Note)
      CONVERT(Chord)
      CONVERT(BarLine)
      CONVERT(Articulation)
      CONVERT(Fermata)
      CONVERT(Marker)
      CONVERT(Clef)
      CONVERT(KeySig)
      CONVERT(TimeSig)
      CONVERT(Measure)
      CONVERT(TempoText)
      CONVERT(Breath)
      CONVERT(HBox)
      CONVERT(VBox)
      CONVERT(TBox)
      CONVERT(FBox)
      CONVERT(Spanner)
      CONVERT(Tie)
      CONVERT(Slur)
      CONVERT(Glissando)
      CONVERT(GlissandoSegment)
      CONVERT(SystemDivider)
      CONVERT(RehearsalMark)
      CONVERT(Harmony)
      CONVERT(Volta)
      CONVERT(Jump)
      CONVERT(StaffText)
      CONVERT(Ottava)
      CONVERT(LayoutBreak)
      CONVERT(Segment)
      CONVERT(Tremolo)
      CONVERT(System)
      CONVERT(Lyrics)
      CONVERT(Stem)
      CONVERT(Beam)
      CONVERT(Hook)
      CONVERT(StemSlash)
      CONVERT(LineSegment)
      CONVERT(SlurSegment)
      CONVERT(TieSegment)
      CONVERT(Spacer)
      CONVERT(StaffLines)
      CONVERT(Ambitus)
      CONVERT(Bracket)
      CONVERT(InstrumentChange)
      CONVERT(StaffTypeChange)
      CONVERT(Text)
      CONVERT(MeasureNumber)
      CONVERT(Hairpin)
      CONVERT(HairpinSegment)
      CONVERT(Bend)
      CONVERT(TremoloBar)
      CONVERT(RepeatMeasure)
      CONVERT(Tuplet)
      CONVERT(NoteDot)
      CONVERT(Dynamic)
      CONVERT(InstrumentName)
      CONVERT(Accidental)
      CONVERT(TextLine)
      CONVERT(TextLineSegment)
      CONVERT(Pedal)
      CONVERT(PedalSegment)
      CONVERT(OttavaSegment)
      CONVERT(LedgerLine)
      CONVERT(Icon)
      CONVERT(VoltaSegment)
      CONVERT(NoteLine)
      CONVERT(Trill)
      CONVERT(TrillSegment)
      CONVERT(LetRing)
      CONVERT(LetRingSegment)
      CONVERT(Vibrato)
      CONVERT(VibratoSegment)
      CONVERT(PalmMute)
      CONVERT(PalmMuteSegment)
      CONVERT(Symbol)
      CONVERT(FSymbol)
      CONVERT(Fingering)
      CONVERT(NoteHead)
      CONVERT(LyricsLine)
      CONVERT(LyricsLineSegment)
      CONVERT(FiguredBass)
      CONVERT(StaffState)
      CONVERT(Arpeggio)
      CONVERT(Image)
      CONVERT(ChordLine)
      CONVERT(FretDiagram)
      CONVERT(Page)
      CONVERT(SystemText)
      CONVERT(BracketItem)
      CONVERT(Staff)
      CONVERT(Part)
      CONVERT(BagpipeEmbellishment)
      CONVERT(Sticking)
#undef CONVERT

}

#endif

