/**
 * Navit, a modular navigation system.
 * Copyright (C) 2005-2008 Navit Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

/** @file
 *
 * @brief Contains code that makes navit able to load maps
 *
 * This file contains the code that makes navit able to load maps. Because
 * navit is able to open maps in different formats, this code does not handle
 * any map format itself. This is done by map plugins which register to this
 * code by calling plugin_register_category_map().
 *
 * When opening a new map, the map plugin will return a pointer to a map_priv
 * struct, which can be defined by the map plugin and contains whatever private
 * data the map plugin needs to access the map. This pointer will also be used
 * as a "handle" to access the map opened.
 *
 * A common task is to create a "map rect". A map rect is a rectangular part of
 * the map, that one can for example retrieve items from. It is not possible to
 * retrieve items directly from the complete map. Creating a map rect returns a
 * pointer to a map_rect_priv, which contains private data for the map rect and
 * will be used as "handle" for this map rect.
 */

#include <glib.h>
#include <string.h>
#include "debug.h"
#include "coord.h"
#include "projection.h"
#include "item.h"
#include "map.h"
#include "maptype.h"
#include "transform.h"
#include "plugin.h"
#include "callback.h"
#include "country.h"
#include "xmlconfig.h"

struct map {
    NAVIT_OBJECT
    struct map_methods meth;			/**< Structure with pointers to the map plugin's functions */
    struct map_priv *priv;				/**< Private data of the map, only known to the map plugin */
    struct callback_list *attr_cbl;		/**< List of callbacks that are called when attributes change */
};

struct map_rect {
    struct map *m;				/**< The map this extract is from */
    struct map_rect_priv *priv; /**< Private data of this map rect, only known to the map plugin */
};

/**
 * @brief Opens a new map
 *
 * This function opens a new map based on the attributes passed. This function
 * takes the attribute "attr_type" to determine which type of map to open and passes
 * all attributes to the map plugin's function that was specified in the
 * plugin_register_new_map_type()-call.
 *
 * Note that every plugin should accept an attribute of type "attr_data" to be passed
 * with the filename of the map to be opened as value.
 *
 * @param attrs Attributes specifying which map to open, see description
 * @return The opened map or NULL on failure
 */
struct map *
map_new(struct attr *parent, struct attr **attrs) {
    struct map *m;
    struct map_priv *(*maptype_new)(struct map_methods *meth, struct attr **attrs, struct callback_list *cbl);
    struct attr *type=attr_search(attrs, attr_type);

    if (! type) {
        dbg(lvl_error,"missing type");
        return NULL;
    }
    maptype_new=plugin_get_category_map(type->u.str);
    if (! maptype_new) {
        dbg(lvl_error,"invalid type '%s'", type->u.str);
        return NULL;
    }

    m=g_new0(struct map, 1);
    m->attrs=attr_list_dup(attrs);
    m->func=&map_func;
    navit_object_ref((struct navit_object *)m);
    m->attr_cbl=callback_list_new();
    m->priv=maptype_new(&m->meth, attrs, m->attr_cbl);
    if (! m->priv) {
        map_destroy(m);
        m=NULL;
    }
    return m;
}

/**
 * @brief Gets an attribute from a map
 *
 * @param this_ The map the attribute should be read from
 * @param type The type of the attribute to be read
 * @param attr Pointer to an attrib-structure where the attribute should be written to
 * @param iter (NOT IMPLEMENTED) Used to iterate through all attributes of a type. Set this to NULL to get the first attribute, set this to an attr_iter to get the next attribute
 * @return True if the attribute type was found, false if not
 */
int map_get_attr(struct map *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter) {
    int ret=0;
    if (this_->meth.map_get_attr)
        ret=this_->meth.map_get_attr(this_->priv, type, attr);
    if (!ret)
        ret=attr_generic_get_attr(this_->attrs, NULL, type, attr, iter);
    if (!ret && type == attr_active) {
        attr->type=type;
        attr->u.num=1;
        return 1;
    }
    return ret;
}

/**
 * @brief Sets an attribute of a map
 *
 * This sets an attribute of a map, overwriting an attribute of the same type if it
 * already exists. This function also calls all the callbacks that are registred
 * to be called when attributes change.
 *
 * @param this_ The map to set the attribute of
 * @param attr The attribute to set
 * @return True if the attr could be set, false otherwise
 */
int map_set_attr(struct map *this_, struct attr *attr) {
    this_->attrs=attr_generic_set_attr(this_->attrs, attr);
    if (this_->meth.map_set_attr)
        this_->meth.map_set_attr(this_->priv, attr);
    callback_list_call_attr_2(this_->attr_cbl, attr->type, this_, attr);
    return 1;
}

/**
 * @brief Registers a new callback for attribute-change
 *
 * This function registers a new callback function that should be called if the attributes
 * of the map change.
 *
 * @param this_ The map to associate the callback with
 * @param cb The callback to add
 */
void map_add_callback(struct map *this_, struct callback *cb) {
    callback_list_add(this_->attr_cbl, cb);
}

/**
 * @brief Removes a callback from the list of attribute-change callbacks
 *
 * This function removes one callback from the list of callbacks functions that should be called
 * when attributes of the map change.
 *
 * @param this_ The map to remove the callback from
 * @param cb The callback to remove
 */
void map_remove_callback(struct map *this_, struct callback *cb) {
    callback_list_remove(this_->attr_cbl, cb);
}


/**
 * @brief Checks if strings from a map have to be converted
 *
 * @param this_ Map to be checked for the need to convert strings
 * @return True if strings from the map have to be converted, false otherwise
 */
int map_requires_conversion(struct map *this_) {
    return (this_->meth.charset != NULL && strcmp(this_->meth.charset, "utf-8"));
}

char *map_converted_string_tmp=NULL;

/**
 * @brief Converts a string from a map into a temporary allocated buffer. Conversion is not performed and original string is returned
 * if map doesn't require conversion. So lifetime of returned value is very limited.
 *
 * @param this_ The map the string to be converted is from
 * @param str The string to be converted
 * @return The converted string. Don't care about it after use.
 */
char *map_convert_string_tmp(struct map *this_, char *str) {
    if(map_converted_string_tmp!=NULL)
        g_free(map_converted_string_tmp);
    map_converted_string_tmp=NULL;
    if(!this_ || !this_->meth.charset || !strcmp(this_->meth.charset, "utf-8"))
        return str;
    map_converted_string_tmp=g_convert(str, -1, "utf-8", this_->meth.charset, NULL, NULL, NULL);
    if(!map_converted_string_tmp) {
        dbg(lvl_error,"Error converting '%s' from %s to utf-8", str, this_->meth.charset);
        return str;
    }
    return map_converted_string_tmp;
}

/**
 * @brief Converts a string from a map
 *
 * @param this_ The map the string to be converted is from
 * @param str The string to be converted
 * @return The converted string. It has to be map_convert_free()d after use.
 */
char *map_convert_string(struct map *this_, char *str) {
    return map_convert_dup(map_convert_string_tmp(this_,str));
}


char *map_convert_dup(char *str) {
    if(map_converted_string_tmp==str) {
        map_converted_string_tmp=NULL;
        return str;
    }
    return g_strdup(str);
}

/**
 * @brief Frees the memory allocated for a converted string
 *
 * @param str The string to be freed
 */
void map_convert_free(char *str) {
    g_free(str);
}

/**
 * @brief Returns the projection of a map
 *
 * @param this_ The map to return the projection of
 * @return The projection of the map
 */
enum projection map_projection(struct map *this_) {
    return this_->meth.pro;
}

/**
 * @brief Sets the projection of a map
 *
 * @param this_ The map to set the projection of
 * @param pro The projection to be set
 */
void map_set_projection(struct map *this_, enum projection pro) {
    this_->meth.pro=pro;
}

/**
 * @brief Destroys an opened map
 *
 * @param m The map to be destroyed
 */

void map_destroy(struct map *m) {
    if (!m)
        return;
    if (m->priv)
        m->meth.map_destroy(m->priv);
    attr_list_free(m->attrs);
    callback_list_destroy(m->attr_cbl);
    g_free(m);
}

/**
 * @brief Creates a new map rect
 *
 * This creates a new map rect, which can be used to retrieve items from a map. If
 * sel is a linked-list of selections, all of them will be used. If you pass NULL as
 * sel, this means "get me the whole map".
 *
 * @param m The map to build the rect on
 * @param sel Map selection to choose the rectangle - may be NULL, see description
 * @return A new map rect
 */
struct map_rect *
map_rect_new(struct map *m, struct map_selection *sel) {
    struct map_rect *mr;

#if 0
    printf("map_rect_new 0x%x,0x%x-0x%x,0x%x\n", r->lu.x, r->lu.y, r->rl.x, r->rl.y);
#endif
    mr=g_new0(struct map_rect, 1);
    mr->m=m;
    mr->priv=m->meth.map_rect_new(m->priv, sel);
    if (! mr->priv) {
        g_free(mr);
        mr=NULL;
    }

    return mr;
}

/**
 * @brief Gets the next item from a map rect
 *
 * Returns an item from a map rect and advances the "item pointer" one step further,
 * so that at the next call the next item is returned. Returns NULL if there are no more items.
 *
 * @param mr The map rect to return an item from
 * @return An item from the map rect
 */
struct item *
map_rect_get_item(struct map_rect *mr) {
    struct item *ret;
    dbg_assert(mr != NULL);
    dbg_assert(mr->m != NULL);
    dbg_assert(mr->m->meth.map_rect_get_item != NULL);
    ret=mr->m->meth.map_rect_get_item(mr->priv);
    if (ret)
        ret->map=mr->m;
    return ret;
}

/**
 * @brief Returns the item specified by the ID
 *
 * Map drivers may or may not allow multiple items with identical IDs. This function is not guaranteed to be
 * suitable for iterating over multiple items with identical IDs in the same manner as `map_rect_get_item()`,
 * as multiple subsequent calls may return items which were already returned by earlier calls.
 *
 * If you are working with maps which allow multiple items with identical IDs, the only portable way to
 * iterate over all items with a given ID is to use `map_rect_get_item()` and skip all items with
 * non-matching IDs.
 *
 * @param mr The map rect to search for the item
 * @param id_hi High part of the ID to be found
 * @param id_lo Low part of the ID to be found
 * @return The item with the specified ID or NULL if not found
 */
struct item *
map_rect_get_item_byid(struct map_rect *mr, int id_hi, int id_lo) {
    struct item *ret=NULL;
    dbg_assert(mr != NULL);
    dbg_assert(mr->m != NULL);
    if (mr->m->meth.map_rect_get_item_byid)
        ret=mr->m->meth.map_rect_get_item_byid(mr->priv, id_hi, id_lo);
    if (ret)
        ret->map=mr->m;
    return ret;
}

/**
 * @brief Destroys a map rect
 *
 * @param mr The map rect to be destroyed
 */
void map_rect_destroy(struct map_rect *mr) {
    if (mr) {
        mr->m->meth.map_rect_destroy(mr->priv);
        g_free(mr);
    }
}

struct map_search {
    struct map *m;
    struct attr search_attr;
    void *priv;
};

/**
 * @brief Starts a search on a map
 *
 * This function starts a search on a map. What attributes one can search for depends on the
 * map plugin.
 *
 * The OSM/binfile plugin currently supports: attr_town_name, attr_street_name
 * The MG plugin currently supports: ttr_town_postal, attr_town_name, attr_street_name
 *
 * If you enable partial matches bear in mind that the search matches only the begin of the
 * strings - a search for a street named "street" would match to "streetfoo", but not to
 * "somestreet". Search is case insensitive.
 *
 * The item passed to this function specifies a "superior item" to "search within" - e.g. a town
 * in which we want to search for a street, or a country in which to search for a town.
 *
 * Please also note that the search for countries is not handled by map plugins but by navit internally -
 * have a look into country.c for details. Because of that every map plugin has to accept a country item
 * to be passed as "superior item".
 *
 * Note: If you change something here, please make sure to also update the documentation of mapset_search_new()
 * in mapset.c!
 *
 * @param m The map that should be searched
 * @param item Specifies a superior item to "search within" (see description)
 * @param search_attr Attribute specifying what to search for. See description.
 * @param partial Set this to true to also have partial matches. See description.
 * @return A new map search struct for this search
 */
struct map_search *
map_search_new(struct map *m, struct item *item, struct attr *search_attr, int partial) {
    struct map_search *this_;
    dbg(lvl_debug,"enter(%p,%p,%p,%d)", m, item, search_attr, partial);
    dbg(lvl_debug,"0x%x 0x%x 0x%x", attr_country_all, search_attr->type, attr_country_name);
    this_=g_new0(struct map_search,1);
    this_->m=m;
    this_->search_attr=*search_attr;
    if ((search_attr->type >= attr_country_all && search_attr->type <= attr_country_name)
            || search_attr->type == attr_country_id)
        this_->priv=country_search_new(&this_->search_attr, partial);
    else {
        if (m->meth.map_search_new) {
            if (m->meth.charset)
                this_->search_attr.u.str=g_convert(this_->search_attr.u.str, -1,m->meth.charset,"utf-8",NULL,NULL,NULL);
            this_->priv=m->meth.map_search_new(m->priv, item, &this_->search_attr, partial);
            if (! this_->priv) {
                g_free(this_);
                this_=NULL;
            }
        } else {
            g_free(this_);
            this_=NULL;
        }
    }
    return this_;
}

/**
 * @brief Returns an item from a map search
 *
 * This returns an item of the result of a search on a map and advances the "item pointer" one step,
 * so that at the next call the next item will be returned. If there are no more items in the result
 * NULL is returned.
 *
 * @param this_ Map search struct of the search
 * @return One item of the result
 */
struct item *
map_search_get_item(struct map_search *this_) {
    struct item *ret;

    if (! this_)
        return NULL;
    if ((this_->search_attr.type >= attr_country_all && this_->search_attr.type <= attr_country_name)
            || this_->search_attr.type == attr_country_id)
        return country_search_get_item(this_->priv);
    ret=this_->m->meth.map_search_get_item(this_->priv);
    if (ret)
        ret->map=this_->m;
    return ret;
}

/**
 * @brief Destroys a map search struct
 *
 * @param this_ The map search struct to be destroyed
 */
void map_search_destroy(struct map_search *this_) {
    if (! this_)
        return;
    if (this_->search_attr.type >= attr_country_all && this_->search_attr.type <= attr_country_name)
        country_search_destroy(this_->priv);
    else {
        if (this_->m->meth.charset)
            g_free(this_->search_attr.u.str);
        this_->m->meth.map_search_destroy(this_->priv);
    }
    g_free(this_);
}

/**
 * @brief Creates a new rectangular map selection
 *
 * @param center Coordinates of the center of the new rectangle
 * @param distance Distance of the rectangle's borders from the center
 * @param order Desired order of the new selection
 * @return The new map selection
 */
struct map_selection *
map_selection_rect_new(struct pcoord *center, int distance, int order) {
    struct map_selection *ret=g_new0(struct map_selection, 1);
    ret->order=order;
    ret->range=item_range_all;
    ret->u.c_rect.lu.x=center->x-distance;
    ret->u.c_rect.lu.y=center->y+distance;
    ret->u.c_rect.rl.x=center->x+distance;
    ret->u.c_rect.rl.y=center->y-distance;
    return ret;
}

/**
 * @brief Duplicates a map selection, transforming coordinates
 *
 * This duplicates a map selection and at the same time transforms the internal
 * coordinates of the selection from one projection to another.
 *
 * @param sel The map selection to be duplicated
 * @param from The projection used for the selection at the moment
 * @param to The projection that should be used for the duplicated selection
 * @return A duplicated, transformed map selection
 */
struct map_selection *
map_selection_dup_pro(struct map_selection *sel, enum projection from, enum projection to) {
    struct map_selection *next,**last;
    struct map_selection *ret=NULL;
    last=&ret;
    while (sel) {
        next = g_new(struct map_selection, 1);
        *next=*sel;
        if (from != projection_none || to != projection_none) {
            transform_from_to(&sel->u.c_rect.lu, from, &next->u.c_rect.lu, to);
            transform_from_to(&sel->u.c_rect.rl, from, &next->u.c_rect.rl, to);
        }
        *last=next;
        last=&next->next;
        sel = sel->next;
    }
    return ret;
}

/**
 * @brief Duplicates a map selection
 *
 * @param sel The map selection to duplicate
 * @return The duplicated map selection
 */
struct map_selection *
map_selection_dup(struct map_selection *sel) {
    return map_selection_dup_pro(sel, projection_none, projection_none);
}

/**
 * @brief Destroys a map selection
 *
 * @param sel The map selection to be destroyed
 */
void map_selection_destroy(struct map_selection *sel) {
    struct map_selection *next;
    while (sel) {
        next = sel->next;
        g_free(sel);
        sel = next;
    }
}

/**
 * @brief Checks if a selection contains a rectangle containing an item
 *
 * This function checks if a selection contains a rectangle which exactly contains
 * an item. The rectangle is automatically built around the given item.
 *
 * @param sel The selection to be checked
 * @param item The item that the rectangle should be built around
 * @return True if the rectangle is within the selection, false otherwise
 */
int map_selection_contains_item_rect(struct map_selection *sel, struct item *item) {
    struct coord c;
    struct coord_rect r;
    int count=0;
    while (item_coord_get(item, &c, 1)) {
        if (! count) {
            r.lu=c;
            r.rl=c;
        } else
            coord_rect_extend(&r, &c);
        count++;
    }
    if (! count)
        return 0;
    return map_selection_contains_rect(sel, &r);

}


/**
 * @brief Checks if a selection contains a item range
 *
 * This function checks if a selection contains at least one of the items in range
 *
 * @param sel The selection to be checked
 * @param follow Whether the next pointer of the selection should be followed
 * @param ranges The item ranges to be checked
 * @count the number of elements in ranges
 * @return True if there is a match, false otherwise
 */

int map_selection_contains_item_range(struct map_selection *sel, int follow, struct item_range *range, int count) {
    int i;
    if (! sel)
        return 1;
    while (sel) {
        for (i = 0 ; i < count ; i++) {
            if (item_range_intersects_range(&sel->range, &range[i]))
                return 1;
        }
        if (! follow)
            break;
        sel=sel->next;
    }
    return 0;
}
/**
 * @brief Checks if a selection contains a item
 *
 * This function checks if a selection contains a item type
 *
 * @param sel The selection to be checked
 * @param follow Whether the next pointer of the selection should be followed
 * @param item The item type to be checked
 * @return True if there is a match, false otherwise
 */

int map_selection_contains_item(struct map_selection *sel, int follow, enum item_type type) {
    if (! sel)
        return 1;
    while (sel) {
        if (item_range_contains_item(&sel->range, type))
            return 1;
        if (! follow)
            break;
        sel=sel->next;
    }
    return 0;
}



/**
 * @brief Checks if a pointer points to the private data of a map
 *
 * @param map The map whose private data should be checked.
 * @param priv The private data that should be checked.
 * @return True if priv is the private data of map
 */
int map_priv_is(struct map *map, struct map_priv *priv) {
    return (map->priv == priv);
}

void map_dump_filedesc(struct map *map, FILE *out) {
    struct map_rect *mr=map_rect_new(map, NULL);
    struct item *item;

    while ((item = map_rect_get_item(mr)))
        item_dump_filedesc(item, map, out);
    map_rect_destroy(mr);
}

void map_dump_file(struct map *map, const char *file) {
    FILE *f;
    f=fopen(file,"w");
    if (f) {
        map_dump_filedesc(map, f);
        fclose(f);
    } else
        dbg(lvl_error,"failed to open file '%s'",file);
}

void map_dump(struct map *map) {
    map_dump_filedesc(map, stdout);
}

struct item *
map_rect_create_item(struct map_rect *mr, enum item_type type_) {
    if(mr && mr->priv && mr->m) {
        return mr->m->meth.map_rect_create_item(mr->priv, type_) ;
    } else {
        return NULL;
    }
}

struct object_func map_func = {
    attr_map,
    (object_func_new)map_new,
    (object_func_get_attr)map_get_attr,
    (object_func_iter_new)NULL,
    (object_func_iter_destroy)NULL,
    (object_func_set_attr)map_set_attr,
    (object_func_add_attr)NULL,
    (object_func_remove_attr)NULL,
    (object_func_init)NULL,
    (object_func_destroy)map_destroy,
    (object_func_dup)NULL,
    (object_func_ref)navit_object_ref,
    (object_func_unref)navit_object_unref,
};



