require "vizshot_gfdnavi"
require "fileutils"

if defined?(Dependencies)
  Dependencies.require_or_load "execute_analysis"
else
  ActiveSupport::Dependencies.require_or_load "execute_analysis"
end

class AnalysisController < ApplicationController
  include ExecuteAnalysis

  layout "gfdnavi"

  before_filter :login_required, :only => :save

  def index
    if session[:analysis]
      @analysis = session[:analysis]
    else
      @analysis = Analysis.new
      @analysis.user = session[:user]
      clear_diagram_files
      clear_temp_variables()
    end
    session[:analysis] = @analysis
    @user = (login=session[:user]) && User.find_by_login(login)
    @variables = all_variables(@user)
    @draw_methods = draw_method_set(@analysis)
    @functions = function_set(@analysis)
    @history = session[:history]
    @diagrams = session[:diagrams]
  end

  def load_history
    i = params[:id].to_i
    unless session[:history][i]
      render :update do |page|
          page.replace_html :messages, "operation failed. please reload the page."
      end
    end
    analysis = session[:history][i][0]
    session[:analysis] = analysis
    redirect_to(:action => "index")
  end

  def clear
    session[:analysis] = nil
    session[:history] = nil
    clear_variables
    clear_diagram_files
    redirect_to(:action => "index")
  end

  def clear_diagrams
    clear_diagram_files
    if request.xhr?
      render(:update){|page|
        page.replace_html :diagrams, ""
      }
    else
      redirect_to(:action => "index")
    end
  end

  def variables_selected
    unless request.xhr?
      clear
      return
    end
    session[:analysis] = variables_set(params["variables"])
    action_type_selected
  end

  def action_type_selected
    unless request.xhr?
      clear
      return
    end
    user = (login=session[:user]) && User.find_by_login(login)
    @analysis = session[:analysis] || Analysis.new
    @analysis.user = session[:user]
    @analysis.action_type = params[:action_type] if params[:action_type]
    if Analysis::ACTION_TYPE[@analysis.action_type] == "draw"
      unless @analysis.draw_method
        dm = nil
        if @analysis.dimensions.length >= 2
          dm = DrawMethod.find(:first,:conditions=>["name=?","tone"],:user=>user)
          dm ||= DrawMethod.find(:first,:conditions=>"nvars>1",:user=>user)
        end
        dm ||= DrawMethod.find(:first,:conditions=>"nvars=1",:user=>user)
        unless dm
          raise "no draw method"
        end
        @analysis.draw_method = dm
      end
      @draw_methods = draw_method_set(@analysis)
    elsif Analysis::ACTION_TYPE[@analysis.action_type] == "analysis"
      @functions = function_set(@analysis)
    end

    session[:analysis] = @analysis
    messages = ""
    if @analysis.variables.length == 0
      @analysis.variable_clear
    elsif @analysis.dimensions.length == 0
      messages = "all selected variables must have the same dimensions"
    end

    render :update do |page|
      page.replace_html :messages, messages
      page.replace_html :dimensions_setting, render(:partial => "dimension_option")
      case Analysis::ACTION_TYPE[@analysis.action_type]
      when "draw"
        page.replace_html :draw_settings, render(:partial => "draw_settings")
        page.replace_html :popular_diagrams, render(:partial => "popular_diagrams")
      when "analysis"
        page.replace_html :analysis_settings, render(:partial => "analysis_settings")
        page.replace_html :popular_diagrams, ""
      end

      page.replace_html :script, <<"EOF"
<script>
  action_type = #{@analysis.action_type};
  executable = #{@analysis.dimensions.length > 0};
  afterCallBack();
</script>
EOF
    end

  end

  def execute
    params_analysis = params[:analysis] || Hash.new
    if params_analysis[:anim]=="1" && (anim = params[:anim])
      val = anim["val"]
      region_org = params_analysis["region"].dup
      anim_dim = params_analysis["anim_dim"]
      params_analysis["region"].delete(anim_dim)
      params_analysis["region"][anim_dim] = {"min"=> val}
      anim = true
    else
      anim = false
    end
    action_type = params[:action_type]
    if request.xhr?
      analysis = session[:analysis]
      unless analysis
        render :update do |page|
          page.replace_html :messages, "operation failed. please reload the page."
        end
        return
      end
    else
      analysis = variables_set(params["variables"])
      analysis.draw_keep = false
      analysis.draw_share = false
      analysis.anim = anim
      analysis.action_type = Analysis::ACTION_TYPE.index(action_type)
    end
    analysis.attributes = params_analysis
    session[:analysis] = analysis
    res = false
    case action_type
    when "draw"
      res = execute_draw(analysis, anim)
      if anim
        analysis.region = region_org
        session[:analysis] = analysis
      end
      return
    when "analysis"
      if request.xhr?
        res = execute_analysis(analysis)
        return
      end
    end
    if res
      session[:history] ||= AnalysisHistory.new
      session[:history].push YAML.load(analysis.to_yaml)
    else
      raise "invalid action (#{action_type})"
    end
  end

  def download_diagram_script_and_data
    id = params[:id]
    diagram = get_temp_diagram(id)
    if diagram
      viz = YAML.load(diagram.vizshot)
      if NumRu::VizShot === viz
        flag = true
        viz.get_variables.each{|v|
          unless v.downloadable?
            flag = false
            break
          end
        }
        if flag
          res = viz_to_script_and_data(viz, work_dir)
          if res[0]
            send_file res[1], :filename => "gfdnavi.tar.gz"
            return
          else
            message = res[1]
          end
        else
          message = "download is inhivited"
        end
      else
        message = "cannot find data to download"
      end
    else
      message = "cannot find diagram"
    end
    flash[:messages] = "failed to download<br/>" + message
    redirect_to :action => "index"
  end

  def upload_file
    user = (login=session[:user]) && User.find_by_login(login)
    if user
      file = params[:file]
      path = File.join(work_dir,File.basename(file.path))
      FileUtils.move(file.path, path)
      session[:temp_variables_list] ||= Array.new
      NumRu::GPhys::IO.var_names_except_coordinates(path).each{|vname|
        gphys = NumRu::GPhys::IO.open(path,vname)
        var = Variable.new
        var.file = "temporary:#{path.sub(/^#{GFDNAVI_WORK_PATH}/,"")}"
        var.path = File.join(var.file, vname)
        var.name = vname
        var.mtime = Time.now
        var.owner = user
        var.size = gphys.length
        node = var.node
        gphys.att_names.each{|an|
          val = gphys.get_att(an)
          val = NArray[val] if Numeric === val
          ka = KeywordAttribute.new
          ka.name = Kconv.kconv(an,Kconv::UTF8)
          ka.value = val
          ka.node = node
          node.keyword_attributes.push(ka)
        }
        session[:temp_variables_list].push var
      }
    end
    redirect_to(:action => "index")
  end

  def download_data
    path = params[:path]
    var = get_temp_variable(path)
    if var
      if var.downloadable?
        fname = var.fname
        if fname
          send_file fname, :filename => "gfdnavi.nc"
          return
        end
      else
        mess = "download is inhivited"
      end
    else
      mess = "cannot download data"
    end
    render :update do |page|
      page.replace_html :messages, mess
    end
  end

  def save
    user = (login=session[:user]) && User.find_by_login(login)
    unless user
      redirect_to :action => "index"
      return
    end
    
    case params[:path]
    when /\.\.\//
      flash[:notice] = '"../" cannot be used in path.'
      redirect_to :action => "save"
      return
    when /^\s*$/
      flash[:notice] = 'Path cannot be empty.'
      redirect_to :action => "save"
      return
    when /^\s*\//, /\/\s*$/, /\/\s*\//
      flash[:notice] = "Directory that doesn't have name is forbidden."
      redirect_to :action => "save"
      return
    end
    
    if request.get?
      @id = params[:id]
      @type = params[:type]
      @groups = user.belonging_groups
      @user = user
      case @type
      when "diagram"
        @suffix = "png"
      when "data"
        @name = get_temp_variable(@id).name
        @suffix = "nc"
      end
      render
      return
    end
    id = params[:id]
    type = params[:type]
#    name = params[:name]
    path = params[:path]
    description = params[:description]
    keywords = params[:keywords]
    groups = params[:groups].values # Hash to Array

    if groups[0] == 'everyone'
      other_mode = 4
      groups.shift
    else
      other_mode = 0
    end
    refs = Array.new
    case type
    when "diagram"
      var = Image.new
      diagram = get_temp_diagram(id)
      viz = YAML.load(diagram.vizshot)
      ids = viz.get_variables.collect{|v|    # このブロックはとりあえずそのままにしているが、要対応
        if Variable===v && ! v.id
          from = v.fname
          to, pa = auto_file_path("data")
          FileUtils.move(from, to)
          v.path = pa + '/' + v.name
          v.owner = user
          v.other_mode = other_mode
          v.set_rgroups(groups) if groups.length > 0
          v.save!
        else
          v = Variable===v ? v : Variable.find(:first, :conditions=>["path=?",v],:user=>user)
        end
        refs << v.node
        v.id
      }
      var.vizshot = viz.to_yaml
      path = "/#{path}.png"
      from = diagram.path
      name = var.name = File.basename(path)
      var.path = File.join("/usr",Knowledge.remove_scheme(user),path)
    when "data"
      var = get_temp_variable(id)
      from = var.fname
      name = var.name
      path = "/#{path}.nc/#{name}"
      var.path = File.join("/usr",Knowledge.remove_scheme(user),path)
      var.file = File.dirname(var.path)
    end
    var.owner = user
    var.other_mode = other_mode
    var.node.set_rgroups(groups) if groups.length > 0

    var.keyword_attributes.build(:name => "description", :value => description) unless description == ""
    keywords.each{|k,v|
      if Hash === v && (name=v["name"])!="" && (val=v["value"])!=""
        if val.to_i.to_s == val
          val = NArray[val.to_i]
        elsif val.to_f.to_s == val
          val = NArray[val.to_f]
        end
        var.keyword_attributes.build(:name => name, :value => val)
      end
    }

    to = var.fname
    FileUtils.makedirs( File.dirname(to) )
    full_path = ""
    parent = nil
    var.path.split(File::Separator)[0..-2].each{|dname|
      full_path = File.join(full_path, dname)
      dir = Directory.find(:first, :conditions=>["path=?",full_path], :user=>user)
      unless dir
        dir = Directory.new
        dir.name = dname
        dir.path = full_path
        dir.parent = parent.node
        dir.owner = user
        dir.other_mode = other_mode
        dir.set_rgroups(groups) if groups.length > 0
        if full_path == var.file
          dir.downloadable = var.downloadable?
          dir.plain_file = true
        end
        dir.save!
      end
      parent = dir
    }

    if var.save
      rtype = {"diagram"=>"draw", "data"=>"analyze"}[type]
      refs.each{|ref|
        NodeRelation.new(:name=>rtype, :reference=>ref, :referenced_by=>var.node).save!
      }
      messages = "successfully saved"
      case type
      when "diagram"
        FileUtils.copy( from, to )
      when "data"
        raise("[BUG] from == to") if from==to
        FileUtils.move( from, to )
        session[:temp_variables_list].delete var
        session[:variables_list] ||= Array.new
        session[:variables_list].push var.path
      end
    else
      messages = "failed to save<br/>"
      messages += var.errors.full_messages.join("<br/>") if var.errors
    end
    flash[:messages] = messages
    redirect_to :action => :index
  end

  def delete_diagram
    id = params[:id].to_i
    if (diagram = session[:diagrams][id])
      session[:diagrams].delete_at(id)
    end
    render :nothing => true
  end

  def pile_up
    unless request.xhr?
      render :nothing => true
      return
    end
    lower_id = params[:lower].to_i
    upper_id = params[:upper].to_i
    if (diagrams = session[:diagrams])
      session_id = session.session_id
      lower = DiagramCache.find_for_session(diagrams[lower_id],session_id)
      upper = DiagramCache.find_for_session(diagrams[upper_id],session_id)
      if lower && upper
        a = YAML.load(lower.vizshot)
        b = YAML.load(upper.vizshot)
        viz = a.add(b)
#        viz = YAML.load(lower.vizshot).add(YAML.load(upper.vizshot))
        res = vizs_to_diagram([viz], true, session[:analysis].draw_share, true)
        show_diagram(res, true, false, true)
        return
      end
    end
    render :nothing => true
  end

  def preview
    unless request.xhr?
      render :nothing => true
      return
    end
    h = params[:function] || params[:draw_method]
    @html = h[:setting_html]
    @analysis = Analysis.new
    render :layout => false
  end

  def show_image
    id = params[:id]
    
    if (dc = DiagramCache.find_for_session(id, session.session_id))
      response.headers['Content-Type'] = 'image/png'
      response.headers['Pragma'] = 'no-cache'
      File.open(dc.path,"rb"){|file|
        render :text => file.read
      }
    else
      render :nothing => true
    end
  end
  
  # end
  private
  def all_variables(user=nil)
    vars = session[:variables_list] || []
    variables = Array.new
    vars.each{|path|
      if v = Variable.find(:first,:conditions=>["path=?",path],:user=>user)
        attrs = v.keyword_attributes.collect{|attr| [attr.name, attr.value]}      
        selected = @analysis.variables.include?(v)
        variables.push [v.name, v.path, attrs, selected, false]
      end
    }
    variables.sort!
    temp_vars = session[:temp_variables_list] || []
    for i in 0...temp_vars.length
      v = temp_vars[i]
      attrs = v.keyword_attributes.collect{|attr| [attr.name, attr.value]}
      selected = @analysis.variables.include?(v)
      variables.push [v.name, "temp_#{i}", attrs, selected, true, v.downloadable?]
    end
    return variables
  end

  def draw_method_set(analysis)
    dms = Array.new
    nvars = analysis.variables.length
    ndims = analysis.dimensions.length
    user = (login=session[:user]) && User.find_by_login(login)
    nvars.times{|i|
      if nvars%(i+1) == 0
        dms += DrawMethod.find(:all, :conditions => ["ndims<=? and nvars=?", ndims, i+1], :user => user)
      end
    }
    return dms
  end

  def function_set(analysis)
    funcs = Array.new
    nvars = analysis.variables.length
    user = (login=session[:user]) && User.find_by_login(login)
    nvars.times{|i|
      if nvars%(i+1) == 0
        funcs += Function.find(:all, :conditions => ["nvars=?", i+1], :user => user)
      end
    }
    return funcs
  end

#draw
  def execute_draw(analysis, anim=false)
    keep = anim || analysis.draw_keep
    res = get_diagram(analysis, keep)
    show_diagram(res, keep, anim)
  end
 
  def show_diagram(res, keep, anim, pile=false)
    unless res[0]
      if request.xhr?
        render :update do |page|
          page.replace_html :messages, res[1]
        end
      else
        render :text => res[1]
      end
      return false
    end
    diagrams = res[1]
    draw_or_cache = res[3]
    if keep && Array === (dgs = session[:diagrams])
      ndg_old = dgs.length
    else
      session[:diagrams] = Array.new
      ndg_old = 0
    end
    diagrams.each{|dc|
      session[:diagrams].push dc.id
    }
    
    ndg_new = diagrams.length
    if request.xhr?
      @analysis = session[:analysis]
      render(:update){|page|
        page.replace_html :messages, ""
        if GFDNAVI_BENCHMARK && !anim
          html = host_information_table
          html +=<<-"EOS"
            Total number of diagrams is #{diagrams.length}<br/>
            Cached diagram: #{draw_or_cache[1]}<br/>
            Drawed diagram: #{draw_or_cache[0]}
          EOS
          page << "benchMark.set(['#{((ndg_old...(ndg_old+ndg_new)).to_a).join('\',\'')}'], '#{escape_javascript(html)}');"
          @benchmark = true
        end
        @anim = anim
        @pile = pile
        diagrams.each_with_index{|dg,i|
          id = ndg_old + i
          @diagram = [id,dg]
          unless dg.diagram_cache_sessions.find(:first,:conditions=>["session=?",session.session_id])
            page.replace_html :messages, "invalid diagram was specified"
            return false
          end
          if keep || i>0
            page.insert_html :bottom, :diagrams, render(:partial => "diagram")
          else
            page.replace_html :diagrams, render(:partial => "diagram")
          end
          if anim
            id_html = "diagram_#{id}"
            page << "anim.diagrams.push($('#{id_html}_table'));"
            page << "anim.next();"
          end
        }
        # "Create Knowledge from Analysis" button is disabled if login menu is disabled.
        page << "drawKnowledgeFromAnalysisButton();" unless GFDNAVI_DISABLE_USER && session[:user].nil?

        @history = session[:history]
        page.replace_html :history, render(:partial => "history")
      }
      return
    else
      @diagrams = diagrams
      render :action => :show_diagram
    end
  end

# analysis
  def execute_analysis(analysis)
    vars, messages = create_new_variable(analysis, work_dir)
    if vars
      session[:temp_variables_list] ||= Array.new
      vars.each{|var| session[:temp_variables_list].push var}
      @analysis = analysis
      user = (login=session[:user]) && User.find_by_login(login)
      @variables = all_variables(user)
      render :update do |page|
        page.replace_html :messages, messages
        page.replace_html :variables_body, render(:partial => "variables")
        if GFDNAVI_BENCHMARK
          html = host_information_table
          html +=<<-"EOS"
            Number of created variable is #{vars.length}.<br/>
          EOS
          page << "benchMark.set(null, '#{escape_javascript(html)}');"
          page << "benchMark.complete();"
        end
        @history = session[:history]
        page.replace_html :history, render(:partial => "history")
      end
      return true
    else
      render :update do |page|
        page.replace_html :messages, messages
      end
      return false
    end
  end


#common
  def get_temp_variable(path)
    variables = session[:temp_variables_list]
    if variables
      /^temp_(.*)/ =~ path
      id = $1.to_i
      return variables[id]
    else
      return nil
    end
  end

  def get_temp_diagram(id)
    diagrams = session[:diagrams]
    if diagrams
      id = id.to_i
      return DiagramCache.find_for_session(diagrams[id],session.session_id)
    else
      return nil
    end
  end

  def clear_temp_variables
    temps = session[:temp_variables_list]
    temps && temps.each{|v|
      File.delete(v.fname) if File.exist?(v.fname)
    }
    session[:temp_variables_list] = nil
  end

  def clear_variables
    clear_temp_variables()
    session[:variables_list] = nil
  end

  def auto_file_path(type)
    case type
    when "data"
      suffix = "nc"
    when "diagram"
      suffix = "png"
    end
    path = user_path+"/auto"
    FileUtils.makedirs(path) unless File.exist?(path)
    last = Dir["#{path}/gfdnavi_\d+\.#{suffix}"][0]
    if last
      last =~ /gfdnavi_(\d+)\./
      num = $1.next
    else
      num = "0000"
    end
    fname = "gfdnavi_#{num}.#{suffix}"
    full_path = "#{path}/#{fname}"
    path = "/usr/#{session[:user]}/auto/#{fname}"
    return [full_path, path]
  end



end
