using System;
using System.Diagnostics;

// Creation date: 17.10.2002
// Checked: xx.05.2002
// Author: Otto Mayer (mot@root.ch)
// Version: 1.01

// Report.NET copyright 2002-2004 root-software ag, Bürglen Switzerland - O. Mayer, S. Spirig, R. Gartenmann, all rights reserved
// This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, version 2.1 of the License.
// 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 Lesser General Public License for more details. You
// should have received a copy of the GNU Lesser 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 www.opensource.org/licenses/lgpl-license.html

namespace Root.Reports {
  /// <summary>Flow Layout Manager</summary>
  public class FlowLayoutManager : LayoutManager {
    /// <summary>Provides data for the NewContainer event.</summary>
    public class NewContainerEventArgs : EventArgs {
      /// <summary>Handle of the flow layout manager</summary>
      public readonly FlowLayoutManager flm;

      /// <summary>Creates a new data object for the NewContainer event.</summary>
      /// <param name="flm">Handle of the flow layout manager</param>
      internal NewContainerEventArgs(FlowLayoutManager flm) {
        this.flm = flm;
      }
    }

    /// <summary>Represents the method that will handle the NewContainer event.</summary>
    public delegate void NewContainerEventHandler(Object oSender, NewContainerEventArgs ea);

    //====================================================================================================x
    /// <summary>Current container that is bound to this layout manager</summary>
    protected Container _container;

    /// <summary>Current horizontal position</summary>
    public Double rX_Cur = 0;

    /// <summary>Current vertical position</summary>
    public Double rY_Cur = 0;

    /// <summary>Occurs when a new container must be created.</summary>
    public event NewContainerEventHandler eNewContainer;

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Creates a new flow layout manager.</summary>
    public FlowLayoutManager() {
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Creates a new flow layout manager.</summary>
    /// <param name="container">Container that must be bound to this layout manager</param>
    public FlowLayoutManager(Container container) : this() {
      this._container = container;
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Gets the current container that is bound to this layout manager</summary>
    public Container container {
      get { return _container; }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Gets the report object of this layout manager.</summary>
    public Report report {
      get { return _container.report; }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Gets or sets the current horizontal position in millimeters.</summary>
    public Double rX_CurMM {
      get { return RT.rMMFromPoint(rX_Cur); }
      set { rX_Cur = RT.rPointFromMM(value); }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Gets or sets the current vertical position in millimeters.</summary>
    public Double rY_CurMM {
      get { return RT.rMMFromPoint(rY_Cur); }
      set { rY_Cur = RT.rPointFromMM(value); }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Adds a report object to the current container at the current position.</summary>
    /// <param name="repObj">Report object to add to the container</param>
    public void Add(RepObj repObj) {
      if (repObj is RepString) {
        RepString repString = (RepString)repObj;
        FontProp fp = repString.fontProp;
        String sText = repString.sText;

        Int32 iLineStartIndex = 0;
        Int32 iIndex = 0;
        while (true) {
          if (rY_Cur > container.rHeight) {
            NewContainer();
          }
          Int32 iLineBreakIndex = 0;
          Double rPosX = rX_Cur;
          Double rLineBreakPos = 0;
          while (true) {
            if (iIndex >= sText.Length) {
              iLineBreakIndex = iIndex;
              rLineBreakPos = rPosX;
              break;
            }
            Char c = sText[iIndex];
            rPosX += fp.rWidth(Convert.ToString(c));
            if (rPosX >= container.rWidth) {
              if (iLineBreakIndex == 0) {
                if (iIndex == iLineStartIndex) {  // at least one character must be printed
                  iIndex++;
                }
                iLineBreakIndex = iIndex;
                break;
              }
              iIndex = iLineBreakIndex;
              break;
            }
            if (c == ' ') {
              iLineBreakIndex = iIndex + 1;
              rLineBreakPos = rPosX;
            }
            else if (c == '\n') {
              iLineBreakIndex = iIndex;
              iIndex++;
              break;
            }
            iIndex++;
          }

          if (iLineStartIndex == 0 && iIndex >= sText.Length) {  // add entire object
            container.Add(rX_Cur, rY_Cur, repObj);
            rX_Cur = rLineBreakPos;
            break;
          }
          String sLine = sText.Substring(iLineStartIndex, iLineBreakIndex - iLineStartIndex);
          container.Add(rX_Cur, rY_Cur, new RepString(fp, sLine));
          if (iIndex >= sText.Length) {
            rX_Cur = rLineBreakPos;
            break;
          }
          rX_Cur = 0;
          rY_Cur += fp.rLineFeed;
          iLineStartIndex = iIndex;
        }
      }
      else {
        Debug.Fail("Unknown object type");
        container.Add(rX_Cur, rY_Cur, repObj);
      }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Adds a report object to the current container on a new line.</summary>
    /// <param name="repString">Report object to add to the container</param>
    public void AddNew(RepString repString) {
      NewLine(repString.fontProp.rLineFeed);
      Add(repString);
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Creates a new container.</summary>
    public void NewContainer() {
      OnNewContainer(new NewContainerEventArgs(this));
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Sets variable _container to the next container.</summary>
    protected override void NextContainer() {
      NewContainer();
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Makes a new line.</summary>
    /// <param name="rLineFeed">Line feed</param>
    public void NewLine(Double rLineFeed) {
      rX_Cur = 0;
      if (rY_Cur + rLineFeed > container.rHeight) {
        NewContainer();
      }
      rY_Cur += rLineFeed;
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Makes a new line (metric version).</summary>
    /// <param name="rLineFeedMM">Line feed in millimeters</param>
    public void NewLineMM(Double rLineFeedMM) {
      NewLine(RT.rPointFromMM(rLineFeedMM));
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Raises the NewContainer event.</summary>
    /// <param name="ea">Event argument</param>
    protected virtual void OnNewContainer(NewContainerEventArgs ea) {
      if (eNewContainer != null) {
        eNewContainer(this, ea);
      }
    }

    //----------------------------------------------------------------------------------------------------x
    /// <summary>Sets a new current container for the layout manager.</summary>
    /// <param name="container">New container</param>
    /// <remarks>The current position will be set to the upper left corner (0, 0).</remarks>
    public void SetContainer(Container container) {
      _container = container;
      rX_Cur = 0;
      rY_Cur = 0;
    }

  }
}
