package osa.attribute.atgrammar.gui.panels.derivation_panels;

import javax.swing.*;

import java.awt.*;

import java.util.*;

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;

import osa.attribute.atgrammar.grammar.*;
import osa.attribute.atgrammar.grammar.symbols.*;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: </p>
 * <p>Company: </p>
 * @author not attributable
 * @version
 */

public class DerivationStringPanel
  extends JPanel
  {

  protected class Arrow
    {
    public int from;
    public int to;
    public int level;

    public Arrow(int from, int to, int level)
      {
      this.from=from;
      this.to=to;
      this.level=level;
      }
    }

  protected class GraphicsSymbol
    {
    public ArrayList arrows=new ArrayList();

    public int width;
    public int height;
    public int x;
    public int y=100;

    public boolean model_is_attribute;

    public ArrayList levels=new ArrayList();

    String text_representation;

    Object model_reference;

    public GraphicsSymbol(Graphics2D g, Object model_reference)
      {
      this.model_reference=model_reference;

      if(model_reference instanceof Attribute)
        {
        model_is_attribute=true;
        text_representation="."+model_reference.toString();
        }
      else
        {
        model_is_attribute=false;
        text_representation=model_reference.toString();
        }
      Font font=g.getFont();
      FontRenderContext context=g.getFontRenderContext();
      Rectangle2D rectangle=font.getStringBounds(text_representation,context);
      Rectangle r=rectangle.getBounds();

      width=r.width;
      height=r.height;
      }
    }

  protected HashMap symbol_to_index;
  protected ArrayList graphics_symbols_string;

  protected DerivationString derivation_string;

  protected static final int HORISONTAL_MARGIN=50;
  protected static final int VERTICAL_MARGIN=50;
  protected static final int SYMBOLS_INTERVAL=5;
  protected static final int ARROWS_INTERVAL=5;

  protected static final Color NON_TERMINALS_COLOR = new Color(128,0,0);
  protected static final Color TERMINALS_COLOR = new Color(0,128,0);
  protected static final Color OUTPUTS_COLOR = new Color(0,0,128);

  public void setModel(DerivationString derivation_string)
    {
    this.derivation_string=derivation_string;
    symbol_to_index=new HashMap();
    graphics_symbols_string=null;
    repaint();
    }

  public DerivationStringPanel()
    {
    super();
    }

  public void paint(Graphics g)
    {
    System.err.println("Paint!!!!!!!!");
    super.paint(g);

    if(derivation_string==null)
      {
      // отображать нечего!
      return;
      }

    Graphics2D g2=(Graphics2D)g.create();

    if(graphics_symbols_string==null)
      {
      graphics_symbols_string=new ArrayList();
      createGraphicsString(g2, derivation_string);
      }

    GraphicsSymbol temp_graphics_symbol;

    int len=graphics_symbols_string.size();
    for(int i=0;i<len;i++)
       {
       temp_graphics_symbol=(GraphicsSymbol)graphics_symbols_string.get(i);

       if(!temp_graphics_symbol.model_is_attribute)
         {
         Symbol symbol_model=(Symbol)temp_graphics_symbol.model_reference;
         switch(symbol_model.getType())
               {
               case Symbol.NON_TERMINAL_SYMBOL:
                 g.setColor(NON_TERMINALS_COLOR);
                 break;
               case Symbol.TERMINAL_SYMBOL:
                 g.setColor(TERMINALS_COLOR);
                 break;
               case Symbol.OUTPUT_SYMBOL:
                 g.setColor(OUTPUTS_COLOR);
                 break;
               default:
               }
         }

        // отображаем символ
        g.drawString(temp_graphics_symbol.text_representation,
                     temp_graphics_symbol.x,
                     temp_graphics_symbol.y);

        // рисуем стрелочки
        this.drawArrows(g, temp_graphics_symbol.arrows);

       }

/*    g.drawString(content,100,100);

    Font font=g.getFont();
    FontRenderContext context=g2.getFontRenderContext();

    Rectangle2D rectangle= font.getStringBounds(content,context);

    Rectangle e=rectangle.getBounds();
    g.drawRect(e.x+100,e.y+100,e.width,e.height);

    g.drawRect(1000,1000,100,100);


    this.setPreferredSize(new Dimension(200000,2000));

    this.revalidate();*/
    }

  protected void createGraphicsString(Graphics2D g, DerivationString string )
    {
    int string_len=string.getLength();
    int horisontal_offset=HORISONTAL_MARGIN;

    Symbol temp_symbol;
    Attribute temp_attribute;
    GraphicsSymbol temp_graphics_symbol;

    int counter=0;

    String text;

    int graphical_symbols_counter;

    for(int symbols_counter=0;symbols_counter<string_len;symbols_counter++)
       {
       // берём символ из строки
       temp_symbol=derivation_string.getSymbolAt(symbols_counter);

       // создаём графический символ
       temp_graphics_symbol=new GraphicsSymbol(g,temp_symbol);
       temp_graphics_symbol.x=horisontal_offset;

       // добавляем символ в графическую строку
       graphics_symbols_string.add(temp_graphics_symbol);
       // инкрементируем счётчик графических символов
       counter++;
       horisontal_offset+=temp_graphics_symbol.width;

       int attributes_count=temp_symbol.getAttrubutesCount();
       for(int attributes_counter=0;attributes_counter<attributes_count;attributes_counter++)
          {
          // берём атрибут символа
          temp_attribute=temp_symbol.getAttributeAt(attributes_counter);
         // создаём графический символ
          temp_graphics_symbol=new GraphicsSymbol(g,temp_attribute);
          temp_graphics_symbol.x=horisontal_offset;
          horisontal_offset+=temp_graphics_symbol.width;

          // добавляем символ в графическую строку
          graphics_symbols_string.add(temp_graphics_symbol);

          // добавляем запись в ассоциативную таблицу "атрибут-индекс в граф.строке"
          symbol_to_index.put(temp_attribute,new Integer(counter));
          }
       // добавляем межсимвольный промежуток
       horisontal_offset+=SYMBOLS_INTERVAL;
       }

    // к этому моменту строка и ассоциативная таблица должны быть сформированы

    // начинаем просчёт стрелочек

    computeArrows();

    }

  public void computeArrows()
    {
    System.err.println("computeArrows()");

    int len=graphics_symbols_string.size();
    GraphicsSymbol temp_graphics_symbol;
    Attribute temp_source_attribute;
    Attribute temp_recipient_attribute;
    ArrayList deferred_assignments;

    int source_index;
    int recipient_index;
    int level;

    // обходим строку
    for(int i=0;i<len;i++)
       {
       // берём очередной символ из строки
       temp_graphics_symbol=(GraphicsSymbol)graphics_symbols_string.get(i);
       // если сей символ отображает атрибут, то это наш клиент ;)
       if(temp_graphics_symbol.model_is_attribute)
         {
         // вытягиваем аттрибут
         temp_source_attribute=(Attribute)temp_graphics_symbol.model_reference;
         System.err.println("Source "+temp_source_attribute.toString());

         source_index=((Integer)symbol_to_index.get(temp_source_attribute)).intValue();
         // вытягиваем у ентого атрибута все его отложенные присваивания
         deferred_assignments=temp_source_attribute.getDeferredAssignments();

         System.err.println("обрабатываем отложенные присваивания");

         // обрабатываем отложенные присваивания
         int deferred_assignments_count=deferred_assignments.size();
         for(int j=0;j<deferred_assignments_count;j++)
            {
            temp_recipient_attribute=(Attribute)deferred_assignments.get(j);

            System.err.println("Recipient "+temp_recipient_attribute.toString());

            recipient_index=((Integer)symbol_to_index.get(temp_recipient_attribute)).intValue();
            // вычисляем и занимаем свободный уровень
            level=reserveLowestFreeLevel(source_index,recipient_index);
            temp_graphics_symbol.arrows.add(new Arrow(source_index,recipient_index,level));
            }
         }
       }
    }



  public int reserveLowestFreeLevel(int begin_index, int end_index )
    {
    GraphicsSymbol temp_graphics_symbol;
    int increment_value;
    int free_level=0;

    if(begin_index<end_index)
      {
      increment_value=1;
      }
    else
      {
      increment_value=-1;
      }

    int temp_levels_count;

    // для начала определим свободный уровень

    for(int i=begin_index;i!=end_index+increment_value;i+=increment_value)
       {
       temp_graphics_symbol=(GraphicsSymbol)graphics_symbols_string.get(i);

       temp_levels_count=temp_graphics_symbol.levels.size()-1;

       // итак, господп мазурики, здесь возможно два случая...

       // первый - требуемый уровень выше чем у символа имеется
       // переходим к следуюсчему символу

       if(temp_levels_count<free_level)
         {
         continue;
         }

      // второй - требуемый уровень занят
      // повышаем уровень

      while(((Boolean)temp_graphics_symbol.levels.get(free_level)).booleanValue())
           {
           free_level++;
           if(temp_levels_count<free_level)
             {
             break;
             }
           }
      }


   // занимаем уровень  ( добавляем недостающие где не хватает )

   for(int i=begin_index;i!=end_index+increment_value;i+=increment_value)
      {
      temp_graphics_symbol=(GraphicsSymbol)graphics_symbols_string.get(i);

      temp_levels_count=temp_graphics_symbol.levels.size()-1;

      for(int j=temp_levels_count;j<free_level;j++)
         {
         temp_graphics_symbol.levels.add(new Boolean(false));
         }

      temp_graphics_symbol.levels.set(free_level,new Boolean(true));
      }

    return free_level;
    }

  public void drawArrows(Graphics g, ArrayList arrows)
    {
    Arrow temp_arrow;
    int len=arrows.size();

    GraphicsSymbol source;
    GraphicsSymbol recipient;

    int arrow_y;
    int arrow_source_x;
    int arrow_destination_x;
    int arrow_height;

    for(int i=0;i<len;i++)
       {
       temp_arrow=(Arrow)arrows.get(i);

       source=(GraphicsSymbol)graphics_symbols_string.get(temp_arrow.from);
       recipient=(GraphicsSymbol)graphics_symbols_string.get(temp_arrow.to);

       System.err.println("Draw arrow!");
       System.err.println("Recipient "+source.text_representation);
       System.err.println("Source "+recipient.text_representation);


       arrow_y=source.y+source.height;
       arrow_source_x=source.x;
       arrow_destination_x=recipient.x;
       arrow_height=ARROWS_INTERVAL*temp_arrow.level+ARROWS_INTERVAL;

       g.drawLine(arrow_source_x,
                  arrow_y,
                  arrow_source_x,
                  arrow_y+arrow_height);

       g.drawLine(arrow_source_x,
                  arrow_y+arrow_height,
                  arrow_destination_x,
                  arrow_y+arrow_height);

       g.drawLine(arrow_destination_x,
                  arrow_y,
                  arrow_destination_x,
                  arrow_y+arrow_height);

       g.drawOval(arrow_destination_x,arrow_y,2,2);

       }
    }


  }