describe "Teacup::Stylesheet" do

  describe 'creation' do
    before do
      @stylesheet = Teacup::Stylesheet.new(:constanttest) { }
    end

    after do
      Teacup::Stylesheet.stylesheets.delete(:constanttest)
    end

    it 'should create constants for named stylesheets' do
      Teacup::Stylesheet[:constanttest].should == @stylesheet
    end

    it 'should allow creating anonymous stylesheets' do
      stylesheet = Teacup::Stylesheet.new { }
      stylesheet.name.should == nil
    end
  end

  describe 'in the simplest cases' do
    before do
      @stylesheet = Teacup::Stylesheet.new do
        style :example_button,
          frame: [[0, 0], [100, 100]]

        style :example_label,
          backgroundColor: :blue

        style :example_textfield, extends: :example_label
      end
    end

    it 'should let you define properties for a stylename' do
      @stylesheet.query(:example_button)[:frame].should == [[0, 0], [100, 100]]
    end

    it 'should let you define properties for many stylenames' do
      @stylesheet.query(:example_label)[:backgroundColor].should == :blue
      @stylesheet.query(:example_textfield)[:backgroundColor].should == :blue
    end

    it 'should return {} for unknown stylenames' do
      @stylesheet.query(:dingbats).should == {}
    end
  end

  describe 'with multiple rules for the same stylename' do
    before do
      @stylesheet = Teacup::Stylesheet.new do
        style :example_button,
          title: "Example!",
          frame: [[0, 0], [100, 100]],
          layer: {
            borderWidth: 10,
            opacity: 0.5
          }

        style :example_button,
          backgroundColor: :blue,
          frame: [[100, 100], [200, 200]],
          layer: {
            borderWidth: 20
          }
      end
    end

    it 'should union different properties' do
      @stylesheet.query(:example_button)[:title].should == "Example!"
      @stylesheet.query(:example_button)[:backgroundColor].should == :blue
    end

    it 'should give later properties precedence' do
      @stylesheet.query(:example_button)[:frame].should == [[100, 100], [200, 200]]
    end

    it 'should merge hashes' do
      @stylesheet.query(:example_button)[:layer][:borderWidth].should == 20
      @stylesheet.query(:example_button)[:layer][:opacity].should == 0.5
    end
  end

  describe 'when a stylename extends another' do
    before do
      @stylesheet = Teacup::Stylesheet.new do
        style :label,
          backgroundColor: :blue,
          font: "IMPACT"

        style :left_label, extends: :label,
          backgroundColor: :green,
          left: 100,
          width: 100

        style :how_much, extends: :left_label,
          backgroundColor: :red,
          top: 200,
          height: 200
      end

    end

    it 'should put extended properties into an "extends" Array of Hashes' do
      @stylesheet.query(:left_label)[:font].should == "IMPACT"
      @stylesheet.query(:left_label)[:left].should == 100
    end

    it 'should give the "lowest" style properties precedence' do
      @stylesheet.query(:left_label)[:backgroundColor].should == :green
    end

    it 'should follow a chain of extends' do
      @stylesheet.query(:how_much)[:backgroundColor].should == :red
      @stylesheet.query(:how_much)[:left] == 100
      @stylesheet.query(:how_much)[:font] == "IMPACT"
    end
  end

  describe 'importing another stylesheet' do

    before do
      @oo_name_importer = Teacup::Stylesheet.new do
        import :importedbyname

        style :label,
          text: @imported_text,
          backgroundColor: :blue,
          layer: {
            borderWidth: 2,
          }
      end

      Teacup::Stylesheet.new(:importedbyname) do
        @imported_text = 'imported text'

        style :label,
          title: "Imported by name",
          layer: {
            borderWidth: 1,
            borderColor: :red,
          }
      end

      @name_importer = Teacup::Stylesheet.new do
        import :importedbyname

        style :label,
          text: @imported_text,
          backgroundColor: :blue
      end

      Teacup::Stylesheet.new(:importrecursion) do
        import :importrecursion

        style :label,
          importtitle: "Import recursion"
      end

      @broken_stylesheet = Teacup::Stylesheet.new do
        import :NonExtant

        style :label,
          backgroundColor: :blue
      end
    end

    after do
      Teacup::Stylesheet.stylesheets.delete(:importedbyname)
      Teacup::Stylesheet.stylesheets.delete(:importrecursion)
    end

    it 'should work with a name' do
      @name_importer.query(:label)[:title].should == "Imported by name"
      @name_importer.query(:label)[:backgroundColor].should == :blue
    end

    it 'should work with a name even if defined out of order' do
      @oo_name_importer.query(:label)[:title].should == "Imported by name"
      @oo_name_importer.query(:label)[:backgroundColor].should == :blue
    end

    it 'should merge properties' do
      @oo_name_importer.query(:label)[:layer][:borderWidth].should == 2
      @oo_name_importer.query(:label)[:layer][:borderColor].should == :red
    end

    it 'should import instance variables' do
      @name_importer.query(:label)[:text].should == 'imported text'
    end

    it 'should import instance variables even if defined out of order' do
      @oo_name_importer.query(:label)[:text].should == 'imported text'
    end

    it 'should work with a value' do
      imported_anonymously = Teacup::Stylesheet.new do
        style :label,
          title: "Imported anonymously"
      end

      anonymous_importer = Teacup::Stylesheet.new do
        import imported_anonymously

        style :label,
          backgroundColor: :blue
      end

      anonymous_importer.query(:label)
      anonymous_importer.query(:label)[:title].should == "Imported anonymously"
      @oo_name_importer.query(:label)[:backgroundColor].should == :blue
    end

    it 'should not explode if a cycle is created' do
      Teacup::Stylesheet[:importrecursion].query(:label)[:importtitle].should == "Import recursion"
    end

    it 'should explode if an unknown stylesheet is imported' do
      error = nil
      begin
        @broken_stylesheet.query(:label)
      rescue Exception => error
      end
      error.nil?.should == false
    end
  end

  describe 'querying imported stylesheets' do
    before do
      @most_generic = Teacup::Stylesheet.new :most_generic do
        style :label,
          backgroundColor: :blue,
          title: "Most generic"

        style :button,
          title: "Click Here!"
      end

      @stylesheet = Teacup::Stylesheet.new :stylesheet do
        import :most_generic

        style :label,
          borderWidth: 10,
          title: "Stylesheet"
      end

      @most_specific = Teacup::Stylesheet.new :most_specific do
        import :stylesheet

        style :label,
          title: "Most specific",
          font: "IMPACT"
      end
    end

    after do
      Teacup::Stylesheet.stylesheets.delete(:most_generic)
      Teacup::Stylesheet.stylesheets.delete(:stylesheet)
      Teacup::Stylesheet.stylesheets.delete(:most_specific)
    end

    it 'should union different properties for the same rule' do
      @stylesheet.query(:label)[:backgroundColor].should == :blue
      @stylesheet.query(:label)[:borderWidth] == 10
    end

    it 'should give the importer precedence' do
      @stylesheet.query(:label)[:title].should == "Stylesheet"
    end

    it 'should follow chains of imports' do
      @most_specific.query(:label)[:title].should == "Most specific"
      @most_specific.query(:label)[:font].should == "IMPACT"
      @most_specific.query(:label)[:borderWidth].should == 10
      @most_specific.query(:label)[:backgroundColor].should == :blue
    end

    it 'should allow querying a rule which exists only in the importee' do
      @stylesheet.query(:button)[:title].should == "Click Here!"
    end
  end

  describe 'precedence tree flattening' do
    it 'should give precedence to later imports' do
      stylesheet = Teacup::Stylesheet.new do
        import Teacup::Stylesheet.new{
          style :label_bla,
            text: "Imported first",
            backgroundColor: :blue
        }

        import Teacup::Stylesheet.new{
          style :label_bla,
            text: "Imported last"
        }
      end
      stylesheet.query(:label_bla)[:text].should == "Imported last"
      stylesheet.query(:label_bla)[:backgroundColor].should == :blue
    end

    it 'should give precedence to less-nested imports' do
      stylesheet = Teacup::Stylesheet.new do
        import Teacup::Stylesheet.new {
          import Teacup::Stylesheet.new {
            style :button,
              text: "Indirect import",
              backgroundColor: :blue
          }

          style :button,
            text: "Direct import"
        }
      end

      stylesheet.query(:button)[:text].should == "Direct import"
      stylesheet.query(:button)[:backgroundColor].should == :blue
    end

    it 'should extend multiple entries, and prefer the first' do
      stylesheet = Teacup::Stylesheet.new do
        style :input,
          origin: [0, 0],
          backgroundColor: :white

        style :textfield,
          text: "Extended",
          backgroundColor: :blue

        style :my_textfield, extends: [:textfield, :input],
          borderWidth: 10
      end

      stylesheet.query(:my_textfield)[:text].should == "Extended"
      stylesheet.query(:my_textfield)[:backgroundColor].should == :blue
      stylesheet.query(:my_textfield)[:origin].should == [0, 0]
      stylesheet.query(:my_textfield)[:borderWidth].should == 10
    end

    it 'should give precedence to imported rules over extended rules' do
      stylesheet = Teacup::Stylesheet.new do
        style :textfield,
          text: "Extended",
          backgroundColor: :blue

        style :my_textfield, extends: :textfield,
          borderWidth: 10

        import Teacup::Stylesheet.new{
          style :my_textfield,
            text: "Imported"
        }
      end

      stylesheet.query(:my_textfield)[:text].should == "Imported"
      stylesheet.query(:my_textfield)[:backgroundColor].should == :blue
      stylesheet.query(:my_textfield)[:borderWidth].should == 10
    end

    it 'should import rules using a deep merge strategy' do
      stylesheet = Teacup::Stylesheet.new do
        import Teacup::Stylesheet.new{
          style :my_textfield,
            layer: {
              borderWidth: 1,
              opacity: 0.5
            }
        }

        style :my_textfield,
          layer: {
            borderWidth: 10
          }
      end

      stylesheet.query(:my_textfield)[:layer][:opacity].should == 0.5
      stylesheet.query(:my_textfield)[:layer][:borderWidth].should == 10
    end

  end
end
