"""This file provides support for displaying Ada expanded code as generated by
   GNAT (-gnatGL switch).
"""


import os, os.path, re, string, distutils.dep_util
import GPS
from gps_utils import *

def create_dg (f, str):
  res=file (f, 'wb')
  first = str.find ("\n", str.find ("\n", str.find ("Source recreated from tree"))+1)+2

  if first > 2:
    last = str.find ("Source recreated from tree", first)
    res.write (str [first:last-1])

  res.close()

expanded_code_marks = {}
# A dictionary that associates a source filename with a list of marks

highlighting = ""

def subprogram_bounds (cursor):
   """Return the first and last line of the current subprogram, and (0,0) if
      the current subprogram could not be determined."""
   blocks = {"CAT_PROCEDURE":1, "CAT_FUNCTION":1, "CAT_ENTRY":1,
             "CAT_PROTECTED":1, "CAT_TASK":1, "CAT_PACKAGE":1}

   if cursor.block_type() == "CAT_UNKNOWN":
      return (0,0)

   min = cursor.buffer().beginning_of_buffer()
   while not blocks.has_key (cursor.block_type()) and cursor > min:
     cursor = cursor.block_start() - 1

   if cursor > min: return (cursor.block_start_line(), cursor.block_end_line())
   else:            return (0,0)

def clear_dg (source_filename):
  """ Clear dg information for filename """
  global expanded_code_marks

  if expanded_code_marks.has_key (source_filename):
    # Remove special lines

    srcbuf = GPS.EditorBuffer.get (GPS.File (source_filename))

    for (mark, lines) in expanded_code_marks [source_filename]:
      srcbuf.remove_special_lines (mark, lines)

    # Empty entry in the dictionary

    expanded_code_marks [source_filename] = []

def edit_dg (dg, source_filename, line, for_subprogram, in_external_editor):
  global highlighting, expanded_code_marks

  # If we are showing the dg in an external editor, simply open this editor and
  # jump to the line
  if in_external_editor:
    buf = GPS.EditorBuffer.get (GPS.File (dg))
    loc = GPS.EditorLocation (buf, 1, 1)
    try:
      (frm, to) = loc.search ("^-- " + `line` + ":", regexp=True)
      if frm:
        buf.current_view().goto (frm.forward_line (1))
    except:
      pass

    return

  # If the highlighting category does not exist, register it now
  if highlighting == "":
    highlighting = "expanded"
    Editor.register_highlighting (highlighting, "#dddddd", False)

  clear_dg (source_filename)

  srcbuf = GPS.EditorBuffer.get (GPS.File (source_filename))

  if for_subprogram:
    (block_first, block_last) = subprogram_bounds (
           srcbuf.current_view().cursor())
  else:
    (block_first, block_last) = (0, 0)

  # Read the text of the dg file
  f=open (dg)
  txt=f.read()
  f.close()

  current_code = ""
  current_line = 1
  lines = 0

  for line in txt.split ("\n"):
    if line.startswith ("-- "):
      if current_code != "":
        if (block_first == 0
            or (current_line > block_first and current_line < block_last)):
          mark = srcbuf.add_special_line (current_line + 1,
                                          current_code,
                                          highlighting, "")

          # Add mark to the list of marks

          if expanded_code_marks.has_key (source_filename):
            expanded_code_marks [source_filename] += [(mark, lines)]
          else:
            expanded_code_marks [source_filename] = [(mark, lines)]

      current_line = int (line [3:line.find(":")])
      current_code = ""
    else:
      if line != "":
        lines = lines + 1
        if current_code == "":
          current_code = line
        else:
          current_code = current_code + "\n" + line

def on_exit (process, status, full_output):
  create_dg (process.dg, full_output)
  edit_dg (process.dg, process.source_filename,
           process.line, process.for_subprogram, process.in_external_editor)

def show_gnatdg(for_subprogram = False, in_external_editor = False):
  """Show the .dg file of the current file"""
  GPS.MDI.save_all (False)
  context = GPS.current_context()
  file = context.file().name()
  line = context.location().line()

  if context.project():
    l = context.project().object_dirs (False)
    prj = " -P" + GPS.Project.root().file().name()
  else:
    l = GPS.Project.root().object_dirs (False)
    prj = " -a"

  if l:
    objdir = l[0]
  else:
    objdir = GPS.get_tmp_dir()
    GPS.Console ("Messages").write (
       "Could not find an object directory for %s, reverting to %s" %
         (file, objdir))

  dg = os.path.join (objdir, os.path.basename (file)) + '.dg'

  if distutils.dep_util.newer (file, dg):
    gnatmake = GPS.Project.root().get_attribute_as_string ("compiler_command",
                 package="ide", index="ada")
    cmd = gnatmake + " -q" + prj + \
          " -f -c -u -gnatcdx -gnatws -gnatGL " + file
    GPS.Console ("Messages").write ("Generating " + dg + "...\n")
    proc = GPS.Process (cmd, on_exit=on_exit)
    proc.source_filename = file
    proc.dg = dg
    proc.line = line
    proc.for_subprogram = for_subprogram
    proc.in_external_editor = in_external_editor
  else:
    edit_dg (dg, file, line, for_subprogram, in_external_editor)

#################################
# Register the contextual menus #
#################################

@interactive ("Ada", in_ada_file, contextual="Expanded code/Show subprogram",
              name="show expanded code for subprogram", before="Align")
def show_gnatdg_subprogram():
  """Show the expanded code of the current subprogram"""
  show_gnatdg(True)

@interactive ("Ada", in_ada_file, contextual="Expanded code/Show entire file",
              name="show expanded code for file", before="Align")
def show_gnatdg_file():
  """Show the .dg file of the current file"""
  show_gnatdg(False)

@interactive ("Ada", in_ada_file, contextual="Expanded code/Show in separate editor",
              name="show expanded code in separate editor", before="Align")
def show_gnatdg_separate_editor():
  """Show the expanded code of the current subprogram"""
  show_gnatdg(False, True)


@interactive ("Ada", in_ada_file, contextual="Expanded code/Clear",
              name="clear expanded code", before="Align")
def clear_expanded_code():
  """Show the expanded code of the current subprogram"""

  context = GPS.current_context()
  clear_dg (context.file().name())
