// Aseprite
// Copyright (C) 2020  Igara Studio S.A.
// Copyright (C) 2001-2018  David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifndef APP_DOC_RANGE_H_INCLUDED
#define APP_DOC_RANGE_H_INCLUDED
#pragma once

#include "doc/frame.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"

#include <iosfwd>

namespace doc {
  class Cel;
  class Sprite;
}

namespace app {
  using namespace doc;

  class DocRange {
  public:
    enum Type { kNone = 0,
                kCels = 1,
                kFrames = 2,
                kLayers = 4 };

    DocRange();
    DocRange(Cel* cel);
    DocRange(const DocRange&) = default;
    DocRange& operator=(const DocRange&) = default;

    Type type() const { return m_type; }
    bool enabled() const { return m_type != kNone; }
    layer_t layers() const { return int(m_selectedLayers.size()); }
    frame_t frames() const { return int(m_selectedFrames.size()); }
    const SelectedLayers& selectedLayers() const  { return m_selectedLayers; }
    const SelectedFrames& selectedFrames() const  { return m_selectedFrames; }

    void setType(const Type type);
    void setSelectedLayers(const SelectedLayers& layers);
    void setSelectedFrames(const SelectedFrames& frames);

    void displace(layer_t layerDelta, frame_t frameDelta);

    bool contains(const Layer* layer) const;
    bool contains(const frame_t frame) const {
      return m_selectedFrames.contains(frame);
    }
    bool contains(const Layer* layer,
                  const frame_t frame) const;

    // If the range includes the given layer, it will be erased from
    // the selection and other candidates might be selected (e.g. a
    // sibling, parent, etc.) using the
    // candidate_if_layer_is_deleted() function.
    void eraseAndAdjust(const Layer* layer);

    void clearRange();
    void startRange(Layer* fromLayer, frame_t fromFrame, Type type);
    void endRange(Layer* toLayer, frame_t toFrame);

    void selectLayer(Layer* layer);
    void selectLayers(const SelectedLayers& selLayers);

    frame_t firstFrame() const { return m_selectedFrames.firstFrame(); }
    frame_t lastFrame() const { return m_selectedFrames.lastFrame(); }

    bool operator==(const DocRange& o) const {
      return (m_type == o.m_type &&
              m_selectedLayers == o.m_selectedLayers &&
              m_selectedFrames == o.m_selectedFrames);
    }

    bool convertToCels(const Sprite* sprite);

    bool write(std::ostream& os) const;
    bool read(std::istream& is);

  private:
    void selectLayerRange(Layer* fromLayer, Layer* toLayer);
    void selectFrameRange(frame_t fromFrame, frame_t toFrame);

    Type m_type;                // Last used type of the range
    int m_flags;                // All used types in startRange()
    SelectedLayers m_selectedLayers;
    SelectedFrames m_selectedFrames;
    Layer* m_selectingFromLayer;
    frame_t m_selectingFromFrame;
  };

} // namespace app

#endif
