require 'zip'

module ProtocolsExporter
  private

  def get_guid(id)
    str1 = '00000000-0000-'
    str2 = id.to_s
    str2 = '0' * (19 - str2.size) + str2
    str2 = '4' + str2
    str2n = str2[0..3] + '-' + str2[4..7] + '-' + str2[8..-1]
    str1 + str2n
  end

  def get_protocol_name(protocol)
    ## "Inject" module's name
    protocol_name = if protocol.in_module? && protocol.name.blank?
                      protocol.my_module.name
                    else
                      protocol.name
                    end
    protocol_name
  end

  def generate_envelope_xml(protocols)
    envelope_xml = "<envelope xmlns=\"http://www.scinote.net\" " \
                   "version=\"1.0\">\n"
    protocols.each do |protocol|
      protocol_name = get_protocol_name(protocol)
      envelope_xml << "<protocol id=\"#{protocol.id}\" " \
                      "guid=\"#{get_guid(protocol.id)}\">#{protocol_name}" \
                      "</protocol>\n"
    end
    envelope_xml << "</envelope>\n"
    envelope_xml
  end

  def tiny_mce_asset_present?(object)
    object.tiny_mce_assets.exists?
  end

  def get_tiny_mce_assets(text)
    return unless text

    regex = Constants::TINY_MCE_ASSET_REGEX
    tiny_assets_xml = "<descriptionAssets>\n"
    text.gsub(regex) do |el|
      match = el.match(regex)
      img = TinyMceAsset.find_by_id(Base62.decode(match[1]))
      next unless img

      img_guid = get_guid(img.id)
      asset_file_name = "rte-#{img_guid}#{File.extname(img.file_name)}"
      asset_xml = "<tinyMceAsset tokenId=\"#{match[1]}\" id=\"#{img.id}\" guid=\"#{img_guid}\" " \
                  "fileRef=\"#{asset_file_name}\">\n"
      asset_xml << "<fileName>#{img.file_name}</fileName>\n"
      asset_xml << "<fileType>#{img.content_type}</fileType>\n"
      asset_xml << "<fileMetadata><!--[CDATA[  #{img.image.metadata.to_json}  ]]--></fileMetadata>\n"
      asset_xml << "</tinyMceAsset>\n"
      tiny_assets_xml << asset_xml
    end
    tiny_assets_xml << "</descriptionAssets>\n"
    tiny_assets_xml
  end

  def generate_protocol_xml(protocol)
    protocol_name = get_protocol_name(protocol)
    protocol_xml = "<eln xmlns=\"http://www.scinote.net\" version=\"1.0\">\n"
    protocol_xml << "<protocol id=\"#{protocol.id}\" " \
                    "guid=\"#{get_guid(protocol.id)}\">\n"
    protocol_xml << "<name>#{protocol_name}</name>\n"
    protocol_xml << "<authors>#{protocol.authors}</authors>\n"
    protocol_xml << "<description>
    <!--[CDATA[  #{Nokogiri::HTML::DocumentFragment.parse(protocol.description)}  ]]-->
    </description>\n"
    if tiny_mce_asset_present?(protocol) && protocol.description
      protocol_xml << get_tiny_mce_assets(protocol.description)
    end
    protocol_xml << "<created_at>#{protocol.created_at.as_json}</created_at>\n"
    protocol_xml << "<updated_at>#{protocol.updated_at.as_json}</updated_at>\n"

    # Steps
    if protocol.steps.count > 0
      protocol_xml << "<steps>\n"
      protocol.steps.order(:id).each do |step|
        step_guid = get_guid(step.id)
        step_xml = "<step id=\"#{step.id}\" guid=\"#{step_guid}\" " \
                   "position=\"#{step.position}\">\n"
        step_xml << "<name>#{step.name}</name>\n"
        # uses 2 spaces to make more difficult to remove user data on import
        step_xml << "<description><!--[CDATA[  #{Nokogiri::HTML::DocumentFragment.parse(step.description)}  ]]--></description>\n"

        if tiny_mce_asset_present?(step)
          step_xml << get_tiny_mce_assets(step.description)
        end
        # Assets
        if step.assets.count > 0
          step_xml << "<assets>\n"
          step.assets.order(:id).each do |asset|
            asset_guid = get_guid(asset.id)
            asset_file_name = "#{asset_guid}" \
                              "#{File.extname(asset.file_name)}"
            asset_xml = "<asset id=\"#{asset.id}\" guid=\"#{asset_guid}\" " \
                        "fileRef=\"#{asset_file_name}\">\n"
            asset_xml << "<fileName>#{asset.file_name}</fileName>\n"
            asset_xml << "<fileType>#{asset.content_type}</fileType>\n"
            asset_xml << "<fileMetadata><!--[CDATA[  #{asset.file.metadata.to_json}  ]]--></fileMetadata>\n"
            asset_xml << "</asset>\n"
            step_xml << asset_xml
          end
          step_xml << "</assets>\n"
        end

        # Tables
        if step.tables.count > 0
          step_xml << "<elnTables>\n"
          step.tables.order(:id).each do |table|
            table_xml = "<elnTable id=\"#{table.id}\" guid=\"#{get_guid(table.id)}\">\n"
            table_xml << "<name>#{table.name}</name>\n"
            table_xml << "<contents>#{table.contents.unpack1('H*')}</contents>\n"
            table_xml << "</elnTable>\n"
            step_xml << table_xml
          end
          step_xml << "</elnTables>\n"
        end

        # Checklists
        if step.checklists.count > 0
          step_xml << "<checklists>\n"
          step.checklists.order(:id).each do |checklist|
            checklist_xml = "<checklist id=\"#{checklist.id}\" " \
                            "guid=\"#{get_guid(checklist.id)}\">\n"
            checklist_xml << "<name>#{checklist.name}</name>\n"
            if checklist.checklist_items
              checklist_xml << "<items>\n"
              checklist.checklist_items.each do |item|
                item_xml = "<item id=\"#{item.id}\" " \
                           "guid=\"#{get_guid(item.id)}\" " \
                           "position=\"#{item.position}\">\n"
                item_xml << "<text>#{item.text}</text>\n"
                item_xml << "</item>\n"
                checklist_xml << item_xml
              end
              checklist_xml << "</items>\n"
            end
            checklist_xml << "</checklist>\n"
            step_xml << checklist_xml
          end
          step_xml << "</checklists>\n"
        end
        protocol_xml << step_xml
        protocol_xml << "</step>\n"
      end
      protocol_xml << "</steps>\n"
    end
    protocol_xml << "</protocol>\n"
    protocol_xml << '</eln>'
    protocol_xml
  end

  def generate_envelope_xsd
    envelope_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    envelope_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" \
                    " elementFormDefault=\"qualified\" " \
                    "attributeFormDefault=\"unqualified\">\n"
    envelope_xsd << "<xs:element name=\"envelope\">\n"
    envelope_xsd << "<xs:complexType>\n"
    envelope_xsd << "<xs:sequence>\n"
    envelope_xsd << "<xs:element name=\"protocol\" maxOccurs=\"unbounded\">\n"
    envelope_xsd << "<xs:complexType>\n"
    envelope_xsd << "<xs:simpleContent>\n"
    envelope_xsd << "<xs:extension base=\"xs:string\">\n"
    envelope_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
                    "use=\"required\"></xs:attribute>\n"
    envelope_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
                    "use=\"required\"></xs:attribute>\n"
    envelope_xsd << "</xs:extension>\n"
    envelope_xsd << "</xs:simpleContent>\n"
    envelope_xsd << "</xs:complexType>\n"
    envelope_xsd << "</xs:element>\n"
    envelope_xsd << "</xs:sequence>\n"
    envelope_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
                    "use=\"required\"></xs:attribute>\n"
    envelope_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
                    "use=\"required\"></xs:attribute>\n"
    envelope_xsd << "</xs:complexType>\n"
    envelope_xsd << "</xs:element>\n"
    envelope_xsd << "</xs:schema>\n"
    envelope_xsd
  end

  def generate_eln_xsd
    eln_xsd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    eln_xsd << "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" " \
               "elementFormDefault=\"qualified\" " \
               "attributeFormDefault=\"unqualified\">\n"
    eln_xsd << "<xs:element name=\"eln\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"protocol\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"authors\" type=\"xs:string\" " \
               "minOccurs=\"0\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
               "minOccurs=\"0\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"created_at\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"updated_at\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"steps\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"step\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"description\" type=\"xs:string\" " \
               "minOccurs=\"0\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"checklists\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"checklist\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"name\" type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"items\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"item\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"text\" type=\"xs:string\"></xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\">" \
               "</xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "<xs:element name=\"descriptionAssets\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"tinyMceAsset\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"fileName\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"fileType\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"tokenId\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"fileRef\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "<xs:element name=\"assets\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"asset\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"fileName\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "<xs:element name=\"fileType\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"fileRef\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "<xs:element name=\"elnTables\" minOccurs=\"0\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:sequence>\n"
    eln_xsd << "<xs:element name=\"elnTable\" maxOccurs=\"unbounded\">\n"
    eln_xsd << "<xs:complexType>\n"
    eln_xsd << "<xs:all>\n"
    eln_xsd << "<xs:element name=\"contents\" " \
               "type=\"xs:string\"></xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"position\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:sequence>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"id\" type=\"xs:int\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"guid\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:all>\n"
    eln_xsd << "<xs:attribute name=\"xmlns\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "<xs:attribute name=\"version\" type=\"xs:string\" " \
               "use=\"required\"></xs:attribute>\n"
    eln_xsd << "</xs:complexType>\n"
    eln_xsd << "</xs:element>\n"
    eln_xsd << "</xs:schema>\n"
  end
end