#!/usr/bin/env python3
# coding=utf-8
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import re
import sys

PAT_GERRIT = re.compile(r'^GERRIT')
PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$')
PAT_GET = re.compile(r'^get::([^ \t\n]*)')
PAT_TITLE = re.compile(r'^\.(.*)')
PAT_STARS = re.compile(r'^\*\*\*\*')
PAT_SEARCHBOX = re.compile(r'^SEARCHBOX')

GERRIT_UPLINK = """

++++
<hr style=\"
  height: 2px;
  color: silver;
  margin-top: 1.2em;
  margin-bottom: 0.5em;
\">
++++

"""

GET_TITLE = '<div class="title">%s</div>'

GET_MACRO = """

++++
<div class="listingblock">
%s
<div class="content">
<a id=\"{0}\" onmousedown="javascript:
  var i = document.URL.lastIndexOf(\'/Documentation/\');
  var url = document.URL.substring(0, i) + \'{0}\';
  document.getElementById(\'{0}\').href = url;">
    GET {0} HTTP/1.0
</a>
</div>
</div>
++++

"""

SEARCH_BOX = """

++++
<div style="
  position:fixed;
  top:0px;
  right:0px;
  text-align:
  right;
  padding-top:2px;
  padding-right:0.5em;
  padding-bottom:2px;">
<input size="40"
  style="line-height: 0.75em;font-size: 0.75em;"
  id="docSearch"
  type="text">
<button style="
  background:none!important;
  border:none;
  padding:0!important;
  vertical-align:bottom;
  font-family:'Open Sans','DejaVu Sans',sans-serif;
  font-size:0.8em;
  color:#1d4b8f;
  text-decoration:none;"
  type="button"
  id="searchBox">
  Search
</button>
  %s
</div>
++++
"""

BUILTIN_SEARCH = """
<script type="text/javascript">
var f = function() {
  window.location = '../#/Documentation/q/' +
    encodeURIComponent(document.getElementById("docSearch").value);
}
document.getElementById("searchBox").onclick = f;
document.getElementById("docSearch").onkeypress = function(e) {
  if (13 == (e.keyCode ? e.keyCode : e.which)) {
    f();
  }
}
</script>
"""

GOOGLE_SITE_SEARCH = """
<script type="text/javascript">
var f = function() {
  window.location = 'https://www.google.com/search?q=' +
     encodeURIComponent(document.getElementById("docSearch").value +
     ' site:@SITE@');
}
document.getElementById("searchBox").onclick = f;
document.getElementById("docSearch").onkeypress = function(e) {
  if (13 == (e.keyCode ? e.keyCode : e.which)) {
    f();
  }
}
</script>
"""


LINK_SCRIPT = """

++++
<script type="text/javascript">
    decorate(document.getElementsByTagName('h1'));
    decorate(document.getElementsByTagName('h2'));
    decorate(document.getElementsByTagName('h3'));
    decorate(document.getElementsByTagName('h4'));

    var divs = document.getElementsByTagName('div');
    var arr = new Array();
    var excluded = getExcludedIds();
    for(var i = 0; i < divs.length; i++) {
      var d = divs[i];
      var id = d.getAttribute('id');
      if (id != null && !(id in excluded)) {
        arr[arr.length] = d;
      }
    }
    decorate(arr);

    var anchors = document.getElementsByTagName('a');
    arr = new Array();
    for(var i = 0; i < anchors.length; i++) {
      var a = anchors[i];
      // if the anchor has no id there is no target to
      // which we can link
      if (a.getAttribute('id') != null) {
        // if the anchor is empty there is no content which
        // can receive the mouseover event, an empty anchor
        // applies to the element that follows, move the
        // element that follows into the anchor so that there
        // is content which can receive the mouseover event
        if (a.firstChild == null) {
          var next = a.nextSibling;
          if (next != null) {
            next.parentNode.removeChild(next);
            a.appendChild(next);
          }
        }
        arr[arr.length] = a;
      }
    }
    decorate(arr);

    function decorate(e) {
      for(var i = 0; i < e.length; i++) {
        e[i].onmouseover = function (evt) {
          var element = this;
          // do nothing if the link icon is currently showing
          var a = element.firstChild;
          if (a != null && a instanceof Element
              && a.getAttribute('id') == 'LINK') {
            return;
          }

          // if there is no id there is no target to link to
          var id = element.getAttribute('id');
          if (id == null) {
            return;
          }

          // create and show a link icon that links to this element
          a = document.createElement('a');
          a.setAttribute('id', 'LINK');
          a.setAttribute('href', '#' + id);
          a.setAttribute('style', 'position: absolute;'
              + ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;'
              + ' padding-left: 4px; padding-right: 4px;');
          var span = document.createElement('span');
          span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;'
              + ' display: inline-block; vertical-align: baseline;'
              + ' font-size: 16px; text-decoration: none; color: grey;');
          a.appendChild(span);
          var link = document.createTextNode('🔗');
          span.appendChild(link);
          element.insertBefore(a, element.firstChild);

          // remove the link icon when the mouse is moved away,
          // but keep it shown if the mouse is over the element, the link or
          // the icon
          hide = function(evt) {
            if (document.elementFromPoint(evt.clientX, evt.clientY) != element
                && document.elementFromPoint(evt.clientX, evt.clientY) != a
                && document.elementFromPoint(evt.clientX, evt.clientY) != span
                && document.elementFromPoint(evt.clientX, evt.clientY) != link
                && element.contains(a)) {
              element.removeChild(a);
            }
          }
          element.onmouseout = hide;
          a.onmouseout = hide;
          span.onmouseout = hide;
          link.onmouseout = hide;
        }
      }
    }

    function getExcludedIds() {
      var excluded = {};
      excluded['header'] = true;
      excluded['toc'] = true;
      excluded['toctitle'] = true;
      excluded['content'] = true;
      excluded['preamble'] = true;
      excluded['footer'] = true;
      excluded['footer-text'] = true;
      return excluded;
    }
</script>

++++

"""

parser = argparse.ArgumentParser()
parser.add_argument('-o', '--out', help='output file')
parser.add_argument('-s', '--src', help='source file')
parser.add_argument('-x', '--suffix', help='suffix for included filenames')
parser.add_argument('-b', '--searchbox', action="store_true", default=True,
                help="generate the search boxes")
parser.add_argument('--no-searchbox', action="store_false", dest='searchbox',
                help="don't generate the search boxes")
parser.add_argument('--site-search', action="store", metavar="SITE",
                help=("generate the search box using google. SITE should " +
                      "point to the domain/path of the site, eg. " +
                      "gerrit-review.googlesource.com/Documentation"))
args = parser.parse_args()

if args.site_search:
  SEARCH_BOX = (SEARCH_BOX %
                GOOGLE_SITE_SEARCH.replace("@SITE@", args.site_search))
else:
  SEARCH_BOX = SEARCH_BOX % BUILTIN_SEARCH


try:
    try:
        out_file = open(args.out, 'w', errors='ignore')
        src_file = open(args.src, 'r', errors='ignore')
    except TypeError:
        out_file = open(args.out, 'w')
        src_file = open(args.src, 'r')
    last_line = ''
    ignore_next_line = False
    last_title = ''
    for line in src_file:
        if PAT_GERRIT.match(last_line):
            # Case of "GERRIT\n------" at the footer
            out_file.write(GERRIT_UPLINK)
            last_line = ''
        elif PAT_SEARCHBOX.match(last_line):
            # Case of 'SEARCHBOX\n---------'
            if args.searchbox:
                out_file.write(SEARCH_BOX)
            last_line = ''
        elif PAT_INCLUDE.match(line):
            # Case of 'include::<filename>'
            match = PAT_INCLUDE.match(line)
            out_file.write(last_line)
            last_line = match.group(1) + args.suffix + match.group(2) + '\n'
        elif PAT_STARS.match(line):
            if PAT_TITLE.match(last_line):
                # Case of the title in '.<title>\n****\nget::<url>\n****'
                match = PAT_TITLE.match(last_line)
                last_title = GET_TITLE % match.group(1)
            else:
                out_file.write(last_line)
                last_title = ''
        elif PAT_GET.match(line):
            # Case of '****\nget::<url>\n****' in rest api
            url = PAT_GET.match(line).group(1)
            out_file.write(GET_MACRO.format(url) % last_title)
            ignore_next_line = True
        elif ignore_next_line:
            # Handle the trailing '****' of the 'get::' case
            last_line = ''
            ignore_next_line = False
        else:
            out_file.write(last_line)
            last_line = line
    out_file.write(last_line)
    out_file.write(LINK_SCRIPT)
    out_file.close()
except IOError as err:
    sys.stderr.write(
        "error while expanding %s to %s: %s" % (args.src, args.out, err))
    exit(1)
