require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
require 'digest/md5'

describe Compass::Sprites do

  before :each do
    create_sprite_temp
    file = StringIO.new("images_path = #{@images_tmp_path.inspect}\n")
    Compass.add_configuration(file, "sprite_config")
    Compass.configure_sass_plugin!
  end

  after :each do
    clean_up_sprites
  end

  def map_location(file)
    Dir.glob(File.join(@images_tmp_path, file)).first
  end

  def image_size(file)
    IO.read(map_location(file))[0x10..0x18].unpack('NN')
  end

  def image_md5(file)
    md5 = Digest::MD5.new
    md5.update IO.read(map_location(file))
    md5.hexdigest
  end

  def render(scss)
    scss = %Q(@import "compass"; #{scss})
    options = Compass.sass_engine_options
    options[:line_comments] = false
    options[:style] = :expanded
    options[:syntax] = :scss
    css = Sass::Engine.new(scss, options).render
    # reformat to fit result of heredoc:
    "      #{css.gsub('@charset "UTF-8";', '').gsub(/\n/, "\n      ").strip}\n"
  end

  #Callbacks
  describe 'callbacks' do
    it "should fire on_sprite_saved" do
      saved = false
      path = nil
      Compass.configuration.on_sprite_saved {|filepath| path = filepath; saved = true }
      render <<-SCSS
        @import "squares/*.png";
        @include all-squares-sprites;
      SCSS
      saved.should eq true
      path.should be_kind_of String
    end
    it "should fire on_sprite_generated" do
      saved = false
      sprite_data = nil
      Compass.configuration.on_sprite_generated {|data| sprite_data = data; saved = true }
      render <<-SCSS
        @import "squares/*.png";
        @include all-squares-sprites;
      SCSS
      sprite_data.should be_kind_of ChunkyPNG::Image
      saved.should eq true
    end
  end

  # DEFAULT USAGE:
  it "should generate sprite classes" do
    css = render <<-SCSS
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-161c60ad78.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -10px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
    image_md5('squares-*.png').should == 'fcc93d7b279c2ad6898fbca49cbd01e1'
  end

  it "should generate sprite classes with dimensions" do
    css = render <<-SCSS
      $squares-sprite-dimensions: true;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-161c60ad78.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
        height: 10px;
        width: 10px;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -10px;
        height: 20px;
        width: 20px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
  end

  it "should provide sprite mixin" do
    css = render <<-SCSS
      @import "squares/*.png";

      .cubicle {
        @include squares-sprite("ten-by-ten");
      }

      .large-cube {
        @include squares-sprite("twenty-by-twenty", true);
      }
    SCSS
    css.should == <<-CSS
      .squares-sprite, .cubicle, .large-cube {
        background: url('/squares-161c60ad78.png') no-repeat;
      }
      
      .cubicle {
        background-position: 0 0;
      }
      
      .large-cube {
        background-position: 0 -10px;
        height: 20px;
        width: 20px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
  end

  # CUSTOMIZATIONS:

  it "should be possible to change the base class" do
    css = render <<-SCSS
      $squares-sprite-base-class: ".circles";
      @import "squares/*.png";
    SCSS
    css.should == <<-CSS
      .circles {
        background: url('/squares-161c60ad78.png') no-repeat;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
  end

  it "should calculate the spacing between images but not before first image" do
    css = render <<-SCSS
      $squares-ten-by-ten-spacing: 33px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-89450808af.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -43px;
      }
    CSS
    image_size('squares-*.png').should == [20, 63]
  end

  it "should calculate the spacing between images" do
    css = render <<-SCSS
      $squares-twenty-by-twenty-spacing: 33px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-673837183a.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -43px;
      }
    CSS
    image_size('squares-*.png').should == [20, 63]
  end

  it "should calculate the maximum spacing between images" do
    css = render <<-SCSS
      $squares-ten-by-ten-spacing: 44px;
      $squares-twenty-by-twenty-spacing: 33px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-1cd84c9068.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -54px;
      }
    CSS
    image_size('squares-*.png').should == [20, 74]
  end

  it "should calculate the maximum spacing between images in reversed order" do
    css = render <<-SCSS
      $squares-ten-by-ten-spacing: 33px;
      $squares-twenty-by-twenty-spacing: 44px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-f25b7090ca.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -54px;
      }
    CSS
    image_size('squares-*.png').should == [20, 74]
  end

  it "should calculate the default spacing between images" do
    css = render <<-SCSS
      $squares-spacing: 22px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-d66bf24bab.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -32px;
      }
    CSS
    image_size('squares-*.png').should == [20, 52]
  end

  it "should use position adjustments in functions" do
    css = render <<-SCSS
      $squares: sprite-map("squares/*.png", $position: 100%);
      .squares-sprite {
        background: $squares no-repeat;
      }

      .adjusted-percentage {
        background-position: sprite-position($squares, ten-by-ten, 100%);
      }

      .adjusted-px-1 {
        background-position: sprite-position($squares, ten-by-ten, 4px);
      }

      .adjusted-px-2 {
        background-position: sprite-position($squares, twenty-by-twenty, -3px, 2px);
      }
    SCSS
    css.should == <<-CSS
      .squares-sprite {
        background: url('/squares-8e490168dd.png') no-repeat;
      }
      
      .adjusted-percentage {
        background-position: 100% 0;
      }
      
      .adjusted-px-1 {
        background-position: -6px 0;
      }
      
      .adjusted-px-2 {
        background-position: -3px -8px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
    image_md5('squares-*.png').should == '652b67f5e9092520d6f26caae7e18012'
  end

  it "should use position adjustments in mixins" do
    css = render <<-SCSS
      $squares-position: 100%;
      @import "squares/*.png";

      .adjusted-percentage {
        @include squares-sprite("ten-by-ten", $offset-x: 100%);
      }

      .adjusted-px-1 {
        @include squares-sprite("ten-by-ten", $offset-x: 4px);
      }

      .adjusted-px-2 {
        @include squares-sprite("twenty-by-twenty", $offset-x: -3px, $offset-y: 2px);
      }
    SCSS
    css.should == <<-CSS
      .squares-sprite, .adjusted-percentage, .adjusted-px-1, .adjusted-px-2 {
        background: url('/squares-8e490168dd.png') no-repeat;
      }
      
      .adjusted-percentage {
        background-position: 100% 0;
      }
      
      .adjusted-px-1 {
        background-position: -6px 0;
      }
      
      .adjusted-px-2 {
        background-position: -3px -8px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
    image_md5('squares-*.png').should == '652b67f5e9092520d6f26caae7e18012'
  end

  it "should repeat the image" do
    css = render <<-SCSS
      $squares-repeat: repeat;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-a5550fd132.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: 0 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: 0 -10px;
      }
    CSS
    image_size('squares-*.png').should == [20, 30]
    image_md5('squares-*.png').should == '94abae8440f1b58617f52920b70aaed2'
  end

  it "should allow the position of a sprite to be specified in absolute pixels" do
    css = render <<-SCSS
      $squares-ten-by-ten-position: 10px;
      $squares-twenty-by-twenty-position: 10px;
      @import "squares/*.png";
      @include all-squares-sprites;
    SCSS
    css.should == <<-CSS
      .squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
        background: url('/squares-89a274044e.png') no-repeat;
      }
      
      .squares-ten-by-ten {
        background-position: -10px 0;
      }
      
      .squares-twenty-by-twenty {
        background-position: -10px -10px;
      }
    CSS
    image_size('squares-*.png').should == [30, 30]
    image_md5('squares-*.png').should == '2fb19ef9c83018c93c6f147af3a56cb2'
  end

  it "should provide a nice errors for lemonade's old users" do
    proc do
      render <<-SCSS
        .squares {
          background: sprite-url("squares/*.png") no-repeat;
        }
      SCSS
    end.should raise_error Sass::SyntaxError,
      %q(The first argument to sprite-url() must be a sprite map. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
    proc do
      render <<-SCSS
        .squares {
          background: sprite-image("squares/twenty-by-twenty.png") no-repeat;
        }
      SCSS
    end.should raise_error Sass::SyntaxError,
      %q(The sprite-image() function has been replaced by sprite(). See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
    proc do
      render <<-SCSS
        @import "squares/*.png";

        .squares {
          background: sprite-position("squares/twenty-by-twenty.png") no-repeat;
        }
      SCSS
    end.should raise_error Sass::SyntaxError,
      %q(The first argument to sprite-position() must be a sprite map. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
  end

  it "should work even if @import is missing" do
    actual_css = render <<-SCSS
      .squares {
        background: sprite(sprite-map("squares/*.png"), twenty-by-twenty) no-repeat;
      }
    SCSS
    actual_css.should == <<-CSS
      .squares {
        background: url('/squares-145869726f.png') 0 -10px no-repeat;
      }
    CSS
  end
  
  it "should calculate corret sprite demsions when givin spacing via issue#253" do
    css = render <<-SCSS
      $squares-spacing: 10px;
      @import "squares/*.png";
      .foo {
        @include sprite-background-position($squares-sprites, "twenty-by-twenty");
      }
      .bar {
        @include sprite-background-position($squares-sprites, "ten-by-ten");
      }
    SCSS
    image_size('squares-*.png').should == [20, 40]
    css.should == <<-CSS
      .squares-sprite {
        background: url('/squares-e3c68372d9.png') no-repeat;
      }
      
      .foo {
        background-position: 0 -20px;
      }
      
      .bar {
        background-position: 0 0;
      }
    CSS
  end

  it "should render corret sprite with css selectors via issue#248" do
    css = render <<-SCSS
      @import "selectors/*.png";
      @include all-selectors-sprites;
    SCSS
    css.should == <<-CSS
      .selectors-sprite, .selectors-ten-by-ten {
        background: url('/selectors-edfef809e2.png') no-repeat;
      }
      
      .selectors-ten-by-ten {
        background-position: 0 0;
      }
      .selectors-ten-by-ten:hover, .selectors-ten-by-ten.ten-by-ten_hover, .selectors-ten-by-ten.ten-by-ten-hover {
        background-position: 0 -20px;
      }
      .selectors-ten-by-ten:target, .selectors-ten-by-ten.ten-by-ten_target, .selectors-ten-by-ten.ten-by-ten-target {
        background-position: 0 -30px;
      }
      .selectors-ten-by-ten:active, .selectors-ten-by-ten.ten-by-ten_active, .selectors-ten-by-ten.ten-by-ten-active {
        background-position: 0 -10px;
      }
    CSS
  end

  it "should render corret sprite with css selectors via magic mixin" do
    css = render <<-SCSS
      @import "selectors/*.png";
      a {
        @include selectors-sprite(ten-by-ten)
      }
    SCSS
    css.should == <<-CSS
      .selectors-sprite, a {
        background: url('/selectors-edfef809e2.png') no-repeat;
      }
      
      a {
        background-position: 0 0;
      }
      a:hover, a.ten-by-ten_hover, a.ten-by-ten-hover {
        background-position: 0 -20px;
      }
      a:target, a.ten-by-ten_target, a.ten-by-ten-target {
        background-position: 0 -30px;
      }
      a:active, a.ten-by-ten_active, a.ten-by-ten-active {
        background-position: 0 -10px;
      }
    CSS
  end
  
  it "should not render corret sprite with css selectors via magic mixin" do
    css = render <<-SCSS
      @import "selectors/*.png";
      a {
        $disable-magic-sprite-selectors:true;
        @include selectors-sprite(ten-by-ten)
      }
    SCSS
    css.should == <<-CSS
      .selectors-sprite, a {
        background: url('/selectors-edfef809e2.png') no-repeat;
      }
      
      a {
        background-position: 0 0;
      }
    CSS
  end
  
  it "should raise error on filenames that are not valid sass syntax" do
    lambda do
      render <<-SCSS
        @import "prefix/*.png";
        a {
          @include squares-sprite(20-by-20);
        }
      SCSS
    end.should raise_error Compass::Error
  end

  it "should generate sprite with bad repeat-x dimensions" do
    css = render <<-SCSS
      $ko-starbg26x27-repeat: repeat-x;
      @import "ko/*.png";
      @include all-ko-sprites;
    SCSS
    css.should == <<-CSS
      .ko-sprite, .ko-default_background, .ko-starbg26x27 {
        background: url('/ko-cc3f80660d.png') no-repeat;
      }
      
      .ko-default_background {
        background-position: 0 0;
      }
      
      .ko-starbg26x27 {
        background-position: 0 -128px;
      }
    CSS
  end

end