require 'rubygems'
require 'inline'

module AttachmentPu
  module ActMethods
    def has_attachment(thumbs_to_generate={})
      cattr_accessor :thumbnails
      self.thumbnails = thumbs_to_generate
      unless included_modules.include?(InstanceMethods)
        after_save :process_attachments
        # extend  ClassMethods
        include InstanceMethods
      end
    end
  end
  
  module InstanceMethods
    def attachment_url(thumb=nil)
      "/attachments/#{thumb ? "#{thumb}_" : ''}#{self.attachment}"
    end

    def attachment_path(thumb=nil)
      "#{MERB_ROOT}/public#{attachment_url(thumb)}"
    end

    def attachment_data=(data)
      ext = data[:filename].match(/\.\w+$/)[0]
      self.attachment = UUID.new + ext
      FileUtils.mv data[:tempfile].path, self.attachment_path
    end
    
    def process_attachments
      self.thumbnails.each do |name, size|
        # begin
          AttachmentPu::GDResize.new.resize(self.attachment_path, self.attachment_path(name.to_sym), size)
        # rescue
        #   puts "Error generating thumbnail of #{self.attachment_path}"
        #   return
        # end
      end
    end
  end

  class GDResize
    SUPPORTED_FORMATS = %w(jpg jpeg png gif)

    def resize_image(filename_in, filename_out, box_w, box_h, image_type, jpeg_quality); end

    def resize(filename_in, filename_out, size, options={})
      options.merge!({
        :cropped => false,
        :jpeg_quality => -1
      })

      ext = filename_in.match(/\.(\w+)$/).to_a.last
      raise "#{ext} extension not recognised. We only support #{SUPPORTED_FORMATS.join(", ")}" unless SUPPORTED_FORMATS.include?(ext)
      resize_image(filename_in, filename_out, size[0], size[1], SUPPORTED_FORMATS.index(ext), options[:jpeg_quality])
    end

    inline do |builder|
      builder.include '"gd.h"'
      builder.add_link_flags "-lgd"

      builder.c <<-"END"
        void resize_image(char *filename_in, char *filename_out, int box_w, int box_h, int image_type, int jpeg_quality) {
          gdImagePtr im_in;
          gdImagePtr im_out;
          int x, y;
          float r;
          FILE *in;
          FILE *out;

          /* Load original file */
          in = fopen(filename_in, "rb");

          /* Support diff image types: jpg jpeg png gif */
          switch(image_type) {
            case 0:
            case 1: im_in = gdImageCreateFromJpeg(in);
                    break;
            case 2: im_in = gdImageCreateFromPng(in);
                    break;
            case 3: im_in = gdImageCreateFromGif(in);
                    break;
            default: puts("Image type not recognised");
          }

          fclose(in);

          /* Only resize if the image is out of bounds - this should be done outside of C */
          if(im_in->sx > box_w || im_in->sy > box_h) {
            /* New image size */
            if(im_in->sx > im_in->sy) {
              r = (float)im_in->sy / (float)im_in->sx;
              x = box_w;
              y = floor(box_h * r);
            } else {
              r = (float)im_in->sx / (float)im_in->sy;
              x = floor(box_w * r);
              y = box_h;
            }
          } else {
            x = im_in->sx;
            y = im_in->sy;
          }

          /* Make the output image four times as small on both axes. Use
            a true color image so that we can interpolate colors. */
          im_out = gdImageCreateTrueColor(x,y);
          /* Now copy the large image, but four times smaller */
          gdImageCopyResampled(im_out, im_in, 0, 0, 0, 0,
            im_out->sx, im_out->sy,
            im_in->sx, im_in->sy);
          out = fopen(filename_out, "wb");

          /* TODO: support diff image types */
          /* Support diff image types: jpg jpeg png gif */
          switch(image_type) {
            case 0:
            case 1: gdImageJpeg(im_out, out, jpeg_quality);
                    break;
            case 2: gdImagePng(im_out, out);
                    break;
            case 3: gdImageGif(im_out, out);
                    break;
            default: puts("Image type not recognised");
          }

          fclose(out);
          gdImageDestroy(im_in);
          gdImageDestroy(im_out);
        }
      END
    end
  end
end

ActiveRecord::Base.send(:extend, AttachmentPu::ActMethods)

