class NodesController < ApplicationController
  layout "gfdnavi"
  session :off

#  before_filter :login_required, :only => [:change_mode, :edit]
  before_filter :basic_authorization_required, :only => [:change_mode, :edit]
  before_filter :basic_authorization, :except => [:change_mode, :edit]


  def index
    user = get_user
    nodes = Node.top_directory_nodes
    list(nodes,user)
  end

  def path
    path = File.join("",params[:path])
    user = get_user
    opts = params.dup
    %w(action controller path).each{|name| opts.delete(name) }
    node = parse_path(path,user,opts)
    case node
    when VirtualNode
      if node.draw_method
        draw(node,user)
      else
        description_methods(node,user)
      end
    when Array
      list(node,user)
    when Node
      if params[:format].nil? && download(node,user)
      else
        description(node,user)
      end
    else
      render_error(:not_found)
    end
  end

  def edit
    flag = false
    if (user = get_user)
      path = File.join("",params[:path])
      node = Node.find(:first, :conditions => ["path=?",path], :user => user)
      if node
        if user.super_user? || node.owner == user
          @node = node
          @groups = user.belonging_groups
          @users = User.find(:all) if user.super_user?
          return
        end
      end
    end
    render_error(:forbidden)
  end

  def update
    flag = false
    if user = get_user
      path = File.join("",params[:path])
      node = Node.find(:first, :conditions => ["path=?",path], :user => user)
      if node
        if user.super_user? || node.owner == user
          flag = true
        end
      end
    end
    unless flag
      render_error(:forbidden)
      return
    end

    if user.super_user?
      owner = User.find(:first, :conditions=>["login=?",params[:user]])
      node.owner = owner if owner
      #gowner = User.find(:first, :conditions=>["login=?",params[:guest_owner_id]])
      #node.guest_owner = gowner if gowner
    end
    node.other_mode = params[:othere_mode].to_i == 4 ? 4 : 0
    if Array === (rgs=params[:rgroups])
      if rgs[0] == 'everyone'
        node.other_mode = 4
        rgs.shift
      else
        node.other_mode = 0
      end
      if rgs.length > 0
        node.set_rgroups(rgs)
      else
        node.rgroups = 0
      end
    else
      node.other_mode = 0
      node.rgroups = 0
    end
    if Array === (wgs=params[:wgroups]) && wgs.length > 0
      node.set_wgroups(wgs)
    else
      node.wgroups = 0
    end
    if node.save
      respons.headers[:Location] = data_url(:path => node.path)
      render :text => "update sucessed", :status => :ok
    else
      render :text => "update failed<br/>\n"+node.errors.messages.join("<br/>\n"), :status => :bad_request
    end
    redirect_to 
  end



  protected


  def parse_path(path,user,opts)
    unless path
      @message ||= Array.new
      @message << "path, #{path}, is invalid"
      return nil
    end
    case path
    when /^(.+)\/\[([\,\d]+)\]$/
      name = $1
      ind = $2
      if ( node = parse_path(name,user,Hash.new) )
        node = VirtualNode.new(node) if Array === node
        res,msg = node[*ind.split(",").collect{|c| c.to_i}]
        if res
          return node
        else
          @message ||= Array.new
          @message << msg
          return nil
        end
      end
    when /^(.+)\/(([\-\+\.\d]+|true|false|\,)+)$/
      name = $1
      val = $2
      if ( node = parse_path(name,user,Hash.new) )
        node = VirtualNode.new(node) if Node === node || Array === node
        ary = val.split(",").collect{|c|
          if /^(true|false)$/ =~ c
             $1 == "true"
          elsif /^(.+)\.\.(.+)$/ =~ c
            ($1.to_f)..($2.to_f)
          else
            c.to_f
          end
        }
        res, msg = node.cut!(*ary)
        if res
          return node
        else
          @message ||= Array.new
          @message << msg
          return nil
        end
      end
    when /^(.+)\/draw\/((?!draw).+)$/
      name = $1
      method = $2
      if ( node = parse_path(name,user,Hash.new) )
        res = parse_method(method,:draw,user,opts)
        if res
          node = VirtualNode.new(node) if Node === node || Array === node
          res,msg = node.draw(*res)
          if res
            return node
          else
            @message ||= Array.new
            @message << msg
            return nil
          end
        end
      end
    when /^(.+)\/analysis\/((?!analysis).+)$/
      name = $1
      method = $2
      if ( node = parse_path(name,user,Hash.new) )
        if /^(.+)\?(.+)$/ =~ method
          method = $1
          opts = ActionController::AbstractRequest.parse_request_parameters(CGI.parse($2))
        end
        res = parse_method(method,:analysis,user,opts)
        if res
          node = VirtualNode.new(node) if Node === node || Array === node
          res,msg = node.function(*res)
          if res
            return node
          else
            @message ||= Array.new
            @message << msg
            return nil
          end
        end
      end
    when /,/
      ary = path.split(",")
      return ary.collect{|pa| parse_path(pa,user,Hash.new) }.flatten
    else
      pa = path
      type = :nothing
      if /^(.*)\/(children|parent)$/ =~ pa
        pa = $1
        type = $2
        pa = "/" if pa == ""
      end
      unless (node=Node.find(:first,:conditions=>["path=?",pa],:user=>user))
        @message ||= Array.new
        @message << "path (#{pa}) cannot be found"
        return nil
      end
      case type
      when :nothing
        return node
      when "children"
        case opts["type"]
        when "directories"
          return node.directory_nodes(:user => user).to_ary
        when "variables"
          return node.variable_nodes(:user => user).to_ary
        when "images"
          return node.image_nodes(:user => user).to_ary
        else
          return node.children(false, :user => user).to_ary
        end
      when "parent"
        return node.parent
      end
    end
    return nil
  end

  def parse_method(path,type,user,opts)
    unless /^\// =~ path
      path = "/"+path
    end
    case type
    when :draw
      if (dm = DrawMethod.find(:first, :conditions=>["path=?",path], :user => user))
        return [dm,opts]
      else
        @message ||= Array.new
        @message << "cannot be found draw method (#{path})"
        return nil
      end
    when :analysis
      if (func = Function.find(:first, :conditions=>["path=?",path], :user => user))
        argc = func.function_arguments.length
        args = Array.new
        if param_args = opts["argv"]
          argc.times{|i|
            if arg = param_args[i.to_s]
              arg = arg.split(",") if /\,/ =~ arg
              args[i] = arg
            end
          }
        end
        args = nil if args.length == 0
        return [func,args]
      else
        @message ||= Array.new
        @message << "cannot be found function (#{path})"
        return nil
      end
    end
    return hash
  end

  def download(node,user)
    if node
      if node.directory?
        dir = node.entity
        if dir.plain_file
          if dir.downloadable?
            send_file dir.fname, :file_name => dir.name, :type => "application/x-netcdf"
            return true
          else
            render_error(:forbidden)
            return true
          end
        end
      elsif node.image?
        img = node.entity
        response.headers['Content-Type'] = img.content_type
        File.open(img.fname,"rb"){|file|
          render :text => file.read
        }
        return true
      end
    end
    return false
  end


  def list(nodes,user)
    if nodes.length > 0
      @nodes = nodes
      respond_to do |format|
        format.html { render :action => :index }
        format.xml { render :xml => nodes.to_xml(:user=>user, :num_dirs=>params[:num_dirs], :uri_prefix=>uri_prefix) }
      end
    else
      render_error(:not_found)
    end
  end

  def description(node,user)
    unless node
      render_error(:not_found)
      return
    end

    respond_to do |format|
      format.xml { render :xml => node.to_xml(:user=>user, :uri_prefix=>uri_prefix) }
      format.html {
        @node = node
        @name = node.name
        @path = node.path
        @type = Node::NODE_TYPES[node.node_type]
        ttl = node.title
        @title = (ttl && ttl!="NULL") ?  ttl : ""
        if user && (user.super_user? || node.owner==user)
          mode = {
            :other_mode => node.other_mode,
            :rgroups => Group.find_by_bit_flag(node.rgroups).collect{|g| g.name}.join(", "),
            :wgroups => Group.find_by_bit_flag(node.wgroups).collect{|g| g.name}.join(", ")
          }
          @mode = mode
        end
        @ancestors = get_descriptions(node)
        if node.directory?
          @directories = node.directory_nodes(:user=>user)
          @variables = node.variable_nodes(:user=>user)
          @images = node.image_nodes(:user=>user)
        end
        @references = node.references(:user=>user).collect{|ref| {"path"=>ref.path}}
        ref_by = node.referenced_by(:user=>user)
        @referenced_by = ref_by.collect{|ref| {"path"=>ref.path}}
        
        # Knowledge <--> Variable , Knowledge <--> Image の関係は
	# node_relations テーブルに入っていないので、別途 find する
	# (Variable <--> Image は大丈夫)
        @link_variables = Array.new
        @link_images = Array.new
        @linked_knowledges = Array.new
	case node.node_type
        when 2 # Image -> Knowledge
          image_id = Image.find(node.id, :user => user).id
          KnowledgeFigure.find(:all, :conditions => ["image_id = ?", image_id]).each do |kf|
	    knowledge = Knowledge.find_by_id(kf.knowledge_id)
            @linked_knowledges.push({"title"=>knowledge.title, "path"=>knowledge.path})
          end
	  @linked_knowledges.uniq!
        when 3 # Knowledge -> Image, Knowledge -> Variable
          Knowledge.find(node.id, :user => user).knowledge_figures.each do |kf|
            image = Image.find_by_id(kf.image_id)
            @link_images.push({"path"=>image.path})
            image.references.each do |v|
              @link_variables.push({"path"=>v.path}) if v
            end
          end
        end
        
=begin
        klg = Array.new
        node.knowledges(:user=>user).each{|kn|
          hash = {"title"=>kn.title, "path"=>kn.path}
          klg.push(hash) unless klg.include?(hash)
        }
        ref_by.each{|rb|
          rb.knowledges(:user=>user).each{|kn|
            hash = {"title"=>kn.title, "path"=>kn.path}
            klg.push(hash) unless klg.include?(hash)
          }
        }
        @knowledges = klg
=end

        if node.directory?
          dir = node.entity
          @plain_file = dir.plain_file
          if dir.downloadable?
            if node.opendap?
              @dl_url = dir.path + '.html'
            else
              @dl_url = data_dl_url(:path => dir.path.sub(/^\//,""))
            end
          end
        elsif node.image?
          @dl_url = data_dl_url(:path => node.path.sub(/^\//,""))
        end
        render :action => "description", :layout => !request.xhr?
      }
    end
  end

  def description_methods(node,user)
#    unless Node === node || VirtualNode === node
    unless VirtualNode === node
      render_error(:bad_request, "no variables were found")
      return
    end
    respond_to{|format|
      format.html {
        @node = node
        render :action => :description_methods
      }
      format.xml {
        render :xml => node.to_xml(:uri_prefix=>uri_prefix)
      }
    }
  end

  def draw(node,user)
    unless VirtualNode === node
      render_error(:bad_request, "no variables were found")
      return
    end
    path = node.path
#    unless ( files = get_diagram_cache(path) )
      files, msg = node.get(GFDNAVI_DIAGRAM_CACHE_PATH)
      unless files
        render_error(:bad_request, msg.gsub(/\n/,"<br\>\n"))
        return
      end
#      put_diagram_cache(node.path, files)
#    end
    respond_to{|format|
      format.html {
        render :text => "ok, #{path}"
      }
      format.xml {
        render :xml => {"status"=>"ok"}.to_xml
      }
    }
  end


  def get_descriptions(node)
    descs = Array.new
    loop do
      h = Hash.new
      h[:type] = Integer=== node.node_type ? Node::NODE_TYPES[node.node_type] : node.node_type
      h[:path] = node.path
      ttl = node.title
      h[:title] = (ttl && ttl!="NULL") ? ttl : node.name
      kwattrs = node.keyword_attributes
      desc = node.description
      h[:description] = (desc && desc!="NULL") ? desc : ''
      h[:name] = node.name
      h[:keyword_attributes] = get_kwattrs(kwattrs, desc)
      info_url = node.stdname("information_url")
      h[:infomation_url] = info_url.value if info_url
      descs.unshift( h )
      node = node.parent
      break unless node
    end
    return descs
  end

  def get_kwattrs(kwattrs, desc)
    ary = Array.new
    kwattrs.each do |att| 
      if att != desc
        v=att.value
        if v.is_a?(NArray)
          if v.length == 1
            v = v[0].to_s
          else
            v = v.to_a.inspect
          end
        end
        ary.push( {:name=>att.name, :value=>v} )
      end
    end
    return ary
  end

  def get_user
#    (login=session[:user]) && User.find_by_login(login)
    (login=basic_login) && User.find_by_login(login)
  end

  def render_error(status, message=nil)
    respond_to do |format|
      format.html {
        text = status.to_s
        text += "<br/>\n" + message if message
        text += "<br/>\n" + @message.join("<br/>\n") if @message
        render :text => text, :status => status
      }
      format.xml { head status }
    end
  end


  def get_diagram_cache(path)
    return nil
    if dc = DiagramCache.find_by_path(path)
      return YAML.loat(dc.files)
    else
      return nil
    end
  end

  def put_diagram_cache(path,files)
    dc = DiagramCache.new(:path => path, :files => files)
    dc.save!
  end



end
