package com.pdflib.cookbook.pcos.interactive;

import java.math.BigDecimal;

import com.pdflib.pCOS;
import com.pdflib.pCOSException;
import com.pdflib.cookbook.pcos.pcos_cookbook_example;

/**
 * Enumerate all links in the document and determine the destination.
 * <p>
 * The program is capable of printing out information about the following
 * kinds of objects:<br>
 * Link annotations containing a destination<br>
 * Link annotations containing a "GoTo", "GoToE", "GoToR" or "URI" action<br>
 *
 * Required software: pCOS interface 3 (pCOS 2.x, PDFlib 7.x, TET 2.2,
 * PLOP 3.x)<br>
 * Required data: PDF document
 *
 * @version $Id: link_destinations.java,v 1.4 2008/07/08 07:10:00 stm Exp $
 */
public class link_destinations extends pcos_cookbook_example {

    /* This is where the data files are. Adjust as necessary. */
    private final static String SEARCH_PATH = "../input";

    public void example_code(pCOS p, String filename) throws pCOSException, Exception {
        /* Open the PDF document */
        int doc = p.open_document(filename, "");
        if (doc == -1)
            throw new Exception("Error: " + p.get_errmsg());

        System.out.println("File name: " + p.get_string(doc, "filename"));

        int pagecount = (int) p.get_number(doc, "length:pages");

        for (int page = 0; page < pagecount; page++) {
            final String annots_path = "pages[" + page + "]/annots";

            String objtype = p.get_string(doc, "type:" + annots_path);
            if (objtype.equals("null"))
                continue;

            int anncount = (int) p.get_number(doc, "length:" + annots_path);
            if (anncount == 0)
                continue;

            System.out.println("Link annotations on page " + (page + 1) + ":");
            for (int ann = 0; ann < anncount; ann++) {
                final String annot_path = annots_path + "[" + ann + "]";
                String subtype = p.get_string(doc, annot_path + "/Subtype");

                if (!subtype.equals("Link"))
                    continue;

                /*
                 * Check whether the annotation is a Link annotation with a
                 * destination.
                 */
                String dest_path = annot_path + "/Dest";
                objtype = p.get_string(doc, "type:" + dest_path);
                if (objtype.equals("string") || objtype.equals("name")
                    || objtype.equals("array")) {
                    System.out.println("\tAnnotation #" + (ann + 1) + ": Link with destination");
                    print_link_info(p, doc, annot_path, dest_path);
                    continue;
                }

                /*
                 * Check whether the annotation is a Link annotation with an
                 * action.
                 */
                String action_path = annot_path + "/A";
                objtype = p.get_string(doc, "type:" + action_path);
                if (objtype.equals("dict")) {
                    System.out.println("\tAnnotation #" + (ann + 1) + ": Link with action");
                    print_link_action(p, doc, annot_path);
                }
            }
        }

        p.close_document(doc);
    }

    /**
     * Print out information about a link.
     * <p>
     * This method implements the different options for the destination
     * syntax that are described in chapter "8.2.1 Destinations" in the
     * Adobe PDF Reference 1.7.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     * @param dest_path The pCOS path to the actual destination description
     *
     * @throws pCOSException
     */
    private void print_link_info(pCOS p, int doc, String annot_path, String dest_path) throws pCOSException {
        print_rect(p, doc, annot_path);

        String contents_path = annot_path + "/Contents";
        String objtype = p.get_string(doc, "type:" + contents_path);
        if (objtype.equals("string")) {
            System.out.println("\t\tDestination contents: \""
                + p.get_string(doc, contents_path) + "\"");
        }

        int dest_page_number = (int) p.get_number(doc, annot_path + "/destpage");
        if (dest_page_number != -1) {
            System.out.println("\t\tDestination page: " + dest_page_number);
        }

        objtype = p.get_string(doc, "type:" + dest_path);
        if (objtype.equals("array")) {
            String dest_kind = p.get_string(doc, dest_path + "[1]");

            System.out.print("\t\t\"" + dest_kind + "\" destination: ");

            if (dest_kind.equals("XYZ")) {
                print_dest_value(p, doc, "left", dest_path + "[2]");
                System.out.print(" ");
                print_dest_value(p, doc, "top", dest_path + "[3]");
                System.out.print(" ");
                print_dest_value(p, doc, "zoom", dest_path + "[4]");
            }
            else if (dest_kind.equals("Fit")) {
                System.out.print("<no parameters>");
            }
            else if (dest_kind.equals("FitH")) {
                print_dest_value(p, doc, "top", dest_path + "[2]");
            }
            else if (dest_kind.equals("FitV")) {
                print_dest_value(p, doc, "left", dest_path + "[2]");
            }
            else if (dest_kind.equals("FitR")) {
                print_dest_value(p, doc, "left", dest_path + "[2]");
                System.out.print(" ");
                print_dest_value(p, doc, "bottom", dest_path + "[3]");
                System.out.print(" ");
                print_dest_value(p, doc, "right", dest_path + "[4]");
                System.out.print(" ");
                print_dest_value(p, doc, "top", dest_path + "[5]");
            }
            else if (dest_kind.equals("FitB")) {
                System.out.print("<no parameters>");
            }
            else if (dest_kind.equals("FitBH")) {
                print_dest_value(p, doc, "top", dest_path + "[2]");
            }
            else if (dest_kind.equals("FitBV")) {
                print_dest_value(p, doc, "left", dest_path + "[2]");
            }
            else {
                System.out.println("illegal destination type!");
            }
            System.out.println();
        }
        else if (objtype.equals("string")) {
            String destination = p.get_string(doc, dest_path);
            System.out.println("\t\tNamed destination (type string): " + destination);
        }
        else if (objtype.equals("name")) {
            String destination = p.get_string(doc, dest_path);
            System.out.println("\t\tNamed destination (type name): " + destination);
        }
        else {
            System.out.println("\t\tIllegal destination type \"" + objtype);
        }
    }

    /**
     * Print a single value in a destination array.
     * <p>
     * The member can either be a NULL object or a number.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param dest_member_name The name of the destination array element
     * @param dest_value_path The pCOS path for the destination array element
     *
     * @throws pCOSException
     */
    private void print_dest_value(pCOS p, int doc, String dest_member_name, String dest_value_path) throws pCOSException {
        System.out.print(dest_member_name + " ");

        String objtype = p.get_string(doc, "type:" + dest_value_path);
        if (objtype.equals("null")) {
            System.out.print("NULL");
        }
        else {
            System.out.print(p.get_number(doc, dest_value_path));
        }
    }

    /**
     * Print out information about the action contained in a Link annotation
     * dictionary.
     * <p>
     * Prints out the "URI", "Goto", "GotoR" and "GotoE" actions stored under
     * the "A" key in a link annotation dictionary (see chapter "8.5.3 Action
     * Types" in the Adobe PDF Reference 1.7).
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_link_action(pCOS p, int doc, String annot_path) throws pCOSException {
        String action_type = p.get_string(doc, annot_path + "/A/S");

        System.out.println("\t\tAction type: \"" + action_type + "\"");

        if (action_type.equals("URI")) {
            print_uri(p, doc, annot_path);
        }
        else if (action_type.equals("GoTo")) {
            print_goto(p, doc, annot_path);
        }
        else if (action_type.equals("GoToR")) {
            print_goto_r(p, doc, annot_path);
        }
        else if (action_type.equals("GoToE")) {
            print_goto_e(p, doc, annot_path);
        }
        else {
            System.out.print("\t\tAction type \"" + action_type
                + "\" is not analyzed in this Cookbook example...");
        }

        System.out.println();
    }

    /**
     * Prints out information about a Link annotation dictionary containing
     * an "URI" action (see "TABLE 8.56 Additional entries specific to a URI
     * action" in the Adobe PDF Reference 1.7).
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_uri(pCOS p, int doc, String annot_path) throws pCOSException {
        print_rect(p, doc, annot_path);

        String uri_path = annot_path + "/A/URI";
        String uri = p.get_string(doc, uri_path);
        System.out.println("\t\tURI: " + uri);

        String ismap_path = annot_path + "/A/IsMap";
        String objtype = p.get_string(doc, "type:" + ismap_path);
        if (objtype.equals("boolean")) {
            System.out.println("\t\tTrack mouse position: "
                + p.get_string(doc, ismap_path));
        }
        else {
            System.out.println("\t\tTrack mouse position: <not specified>");
        }
    }

    /**
     * Print out the "Rect" dictionary member for an annotation dictionary.
     * The numbers are rounded to two digits after the decimal point.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_rect(pCOS p, int doc, String annot_path)
        throws pCOSException {
        System.out.print("\t\tAnnotation rectangle: ");
        for (int i = 0; i < 4; i++) {
            BigDecimal value = new BigDecimal(p.get_number(doc, annot_path + "/Rect[" + i + "]"));
            BigDecimal roundedValue = value.setScale(2, BigDecimal.ROUND_HALF_UP);
            System.out.print(roundedValue.toString() + " ");
        }
        System.out.println();
    }

    /**
     * Print out information about a Link annotation dictionary containing
     * a "GoTo" action (see "TABLE 8.49 Additional entries specific to a
     * go-to action" in the Adobe PDF Reference 1.7).
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_goto(pCOS p, int doc, String annot_path) throws pCOSException {
        print_action_info(p, doc, annot_path);
    }

    /**
     * Print out information about a link annotation with a "GoToE" action (see
     * "TABLE 8.51 Additional entries specific to an embedded go-to action" in
     * the Adobe PDF Reference 1.7).
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_goto_e(pCOS p, int doc, String annot_path) throws pCOSException {
        print_goto_r_e_common(p, doc, annot_path);
        print_target_dictionary(p, doc, annot_path + "/A/T", "");
    }

    /**
     * Print out information about a Link annotation dictionary containing
     * a "GoToR" action (see "TABLE 8.50 Additional entries specific to a
     * remote go-to action" in the Adobe PDF Reference 1.7).
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_goto_r(pCOS p, int doc, String annot_path) throws pCOSException {
        print_goto_r_e_common(p, doc, annot_path);
    }

    /**
     * Print the common information for the "GoToR" and "GoToE" actions.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_goto_r_e_common(pCOS p, int doc, String annot_path)
        throws pCOSException {
        print_action_info(p, doc, annot_path);
        print_filespec(p, doc, annot_path);

        System.out.print("\t\tOpen destination in new window: ");
        String new_window_path = annot_path + "/A/NewWindow";
        if (p.get_string(doc, "type:" + new_window_path).equals("boolean")) {
            System.out.println(p.get_string(doc, new_window_path));
        }
        else {
            System.out.println("<not specified>");
        }
    }

    /**
     * Print out information about a target dictionary (see "TABLE 8.52 Entries
     * specific to a target dictionary" in the Adobe PDF Reference 1.7). Target
     * dictionaries can contain target dictionaries, so this routine is
     * recursive.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     * @param tabs A string of \t characters to indent in recursive invocations
     *
     * @throws pCOSException
     */
    private void print_target_dictionary(pCOS p, int doc, String target_dict_path, String tabs) throws pCOSException {
        String objtype = p.get_string(doc, "type:" + target_dict_path);

        if (objtype.equals("dict")) {
            System.out.println(tabs + "\t\tTarget dictionary:");

            String relationship_path = target_dict_path + "/R";
            String relationship = p.get_string(doc, relationship_path);
            if (relationship.equals("P")) {
                System.out.println(tabs + "\t\t\tRelationship: Target is parent of current document");
            }
            else if (relationship.equals("C")) {
                System.out.println(tabs + "\t\t\tRelationship: Target is child of current document");

                String embedded_files_name_path = target_dict_path + "/N";
                objtype = p.get_string(doc, "type:" + embedded_files_name_path);
                if (objtype.equals("string")) {
                    System.out.println(tabs + "\t\t\tName of file in EmbeddedFiles name tree: "
                        + p.get_string(doc, embedded_files_name_path));
                }

                String file_attachment_info_path = target_dict_path + "/P";
                objtype = p.get_string(doc, "type:" + file_attachment_info_path);
                if (objtype.equals("string")) {
                    System.out.println(tabs + "\t\t\tNamed destination for page number of file attachment annotation: "
                        + p.get_string(doc, file_attachment_info_path));
                }
                else if (objtype.equals("number")) {
                    int page_number_of_file_attachment_annotation =
                        (int) p.get_number(doc, file_attachment_info_path);
                    System.out.println(tabs + "\t\t\tPage number of file attachment annotation: "
                        + (page_number_of_file_attachment_annotation + 1));
                }

                String annot_identifier_path = target_dict_path + "/A";
                objtype = p.get_string(doc, "type:" + annot_identifier_path);
                if (objtype.equals("string")) {
                    System.out.println(tabs + "\t\t\tUnique annotation identifier: "
                        + p.get_string(doc, annot_identifier_path));
                }
                else if (objtype.equals("number")) {
                    int annot_index = (int) p.get_number(doc, annot_identifier_path);
                    System.out.println(tabs + "\t\t\tIndex in \"Annots\" array: "
                        + annot_index);
                }

                /*
                 * Recursive invocation to resolve embedded target dictionaries.
                 * The routine will simple return if there is no target
                 * dictionary present.
                 */
                print_target_dictionary(p, doc, target_dict_path + "/T",
                    tabs + "\t");
            }
            else {
                System.out.println("\t\t\tRelationship: Illegal value "
                    + relationship);
            }
        }
    }

    /**
     * Print out a file specification dictionary (see chapter "3.10.2 File
     * Specification Dictionaries" in the Adobe PDF Reference 1.7).
     * <p>
     * This implementation only looks at the "F", "DOS", "Mac" and "Unix" keys
     * of the dictionary.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_filespec(pCOS p, int doc, String annot_path) throws pCOSException {
        String filespec_path = annot_path + "/A/F";

        String objtype = p.get_string(doc, "type:" + filespec_path);

        if (objtype.equals("dict")) {
            System.out.println("\t\tFile specification:");

            print_filespec_member(p, doc, filespec_path, "DOS");
            print_filespec_member(p, doc, filespec_path, "Mac");
            print_filespec_member(p, doc, filespec_path, "Unix");
            print_filespec_member(p, doc, filespec_path, "F");
        }
    }

    /**
     * Prints out a member of a file specification dictionary.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param filespec_path The path to the file specification dictionary
     * @param platform The member of the file specification dictionary to print
     *
     * @throws pCOSException
     */
    private void print_filespec_member(pCOS p, int doc, String filespec_path,
            String platform)
        throws pCOSException {
        String platform_path = filespec_path + "/" + platform;
        String objtype = p.get_string(doc, "type:" + platform_path);
        if (objtype.equals("string")) {
            System.out.println("\t\t\t" + platform +": "
                + p.get_string(doc, platform_path));
        }
    }

    /**
     * Print common information about a Link annotation dictionary containing
     * an action.
     *
     * @param p A pCOS object
     * @param doc A valid pCOS document handle
     * @param annot_path The pCOS path to the link annotation dictionary
     *
     * @throws pCOSException
     */
    private void print_action_info(pCOS p, int doc, String annot_path) throws pCOSException {
        print_link_info(p, doc, annot_path, annot_path + "/A/D");
    }

    public link_destinations(String[] argv, String readable_name,
        String search_path, String full_rcs_file_name, String revision) {
        super(argv, readable_name, search_path, full_rcs_file_name, revision);
    }

    public static void main(String argv[]) {
        link_destinations example = new link_destinations(argv,
            "Link destinations", SEARCH_PATH,
            "$RCSfile: link_destinations.java,v $", "$Revision: 1.4 $");
        example.execute();
    }
}
