#include "stdafx.h"
#include "Match.h"
#include "GroupNotFoundException.h"

namespace NppPythonScript
{


boost::python::str Match::py_group_number(int groupNumber)
{
    GroupDetail *groupDetail = group(groupNumber);
    if (NULL == groupDetail)
	{
        throw GroupNotFoundException("no such group");
	}

    return boost::python::str(getTextForGroup(group(groupNumber)));
}


boost::python::str Match::py_group_name(boost::python::str pyGroupName)
{
    std::string stringGroupName(boost::python::extract<const char *>(pyGroupName.attr("__str__")()));
    
    GroupDetail *groupDetail = groupName(stringGroupName.c_str());
    if (NULL == groupDetail)
	{
        throw GroupNotFoundException("no such group");
	}

    return boost::python::str(getTextForGroup(groupDetail));
}

boost::python::str Match::getGroup(boost::python::object groupIdentifier)
{
    if (PyInt_Check(groupIdentifier.ptr()))
	{
        return py_group_number(boost::python::extract<int>(groupIdentifier));
	}
	else if (PyString_Check(groupIdentifier.ptr()))
	{
        return py_group_name(boost::python::extract<boost::python::str>(groupIdentifier));
	} 
	else 
	{
        return py_group_name(boost::python::extract<boost::python::str>(groupIdentifier.attr("__str__")));
	}
}



boost::python::str Match::py_expand(boost::python::object replaceFormat)
{
	char *result;
    size_t resultLength;
	expand(boost::python::extract<const char *>(replaceFormat.attr("__str__")()), &result, &resultLength);
    boost::python::str pyResult(const_cast<const char *>(result));
    delete [] result;
    return pyResult;
}


int Match::py_start(int groupIndex)
{
    GroupDetail *groupDetail = group(groupIndex);
    int result = -1;
	if (groupDetail && groupDetail->matched()) 
	{
        result = groupDetail->start();
	}

    return result;   
}

int Match::py_start_name(boost::python::str groupName)
{
    GroupDetail *groupDetail = this->groupName(boost::python::extract<const char *>(groupName));
    int result = -1;
	if (groupDetail && groupDetail->matched()) 
	{
        result = groupDetail->start();
	}
    return result;
}

int Match::py_end(int groupIndex)
{
    GroupDetail *groupDetail = group(groupIndex);
    int result = -1;
	if (groupDetail && groupDetail->matched()) 
	{
        result = groupDetail->end();
	}

    return result;   
}

int Match::py_end_name(boost::python::str groupName)
{
    GroupDetail *groupDetail = this->groupName(boost::python::extract<const char *>(groupName));
    int result = -1;
	if (groupDetail && groupDetail->matched()) 
	{
        result = groupDetail->end();
	}
    return result;
}

boost::python::tuple Match::py_span(int groupIndex)
{
    return boost::python::make_tuple(py_start(groupIndex), py_end(groupIndex));
}

boost::python::tuple Match::py_span_name(boost::python::str groupName)
{
    return boost::python::make_tuple(py_start_name(groupName), py_end_name(groupName));
}
        
int Match::py_lastindex()
{
    int lastGroup = groupCount() - 1;
    while(lastGroup > 0 && !group(lastGroup)->matched())
        --lastGroup;

    return lastGroup;
}

boost::python::tuple Match::py_groups()
{
	size_t size = groupCount();
    PyObject* groupsTuple = PyTuple_New(size - 1);
    for(int index = 1; index != size; ++index)
	{
        boost::python::str groupContent = py_group_number(index);
        // PyTuple_SetItem steals a reference, but because it's a boost::python::object, it'll be Py_DECREF'd by the next iteration
        Py_INCREF(groupContent.ptr());
        PyTuple_SetItem(groupsTuple, index - 1, groupContent.ptr());
	}

    return boost::python::tuple(boost::python::handle<PyObject>(groupsTuple));
}

boost::python::object py_group_variable(boost::python::tuple args, boost::python::dict kwargs)
{
    Match *match = boost::python::extract<Match*>(args[0]);
    return match->py_group_variable(boost::python::tuple(args.slice(1, boost::python::len(args))), kwargs);
}

boost::python::object Match::py_group_variable(boost::python::tuple args, boost::python::dict kwargs)
{
    size_t size = boost::python::len(args); 

    // For the default case, no arguments, return the whole match
    if (size == 0)
	{
        return py_group_number(0);
	}

    // If only one argument is specified, then we return the content of that group
    if (size == 1)
	{
        if (PyNumber_Check(boost::python::object(args[0]).ptr()))
		{
            return py_group_number(boost::python::extract<int>(args[0]));
		}
		else
		{
            return py_group_name(boost::python::str(args[0]));
		}
	}

    // If more than one argument is specified, then we return a tuple with group contents in order of the arguments specified
    PyObject* groupsTuple = PyTuple_New(size);
    for(int index = 0; index != size; ++index)
	{
        boost::python::str groupContent;
        if (PyNumber_Check(boost::python::object(args[index]).ptr()))
		{
            groupContent = py_group_number(boost::python::extract<int>(args[index]));
		}
		else
		{
            groupContent = py_group_name(boost::python::str(args[index]));
		}

        // PyTuple_SetItem steals a reference, but because it's a boost::python::object, it'll be Py_DECREF'd by the next iteration
        Py_INCREF(groupContent.ptr());
        PyTuple_SetItem(groupsTuple, index, groupContent.ptr());
	}
    return boost::python::tuple(boost::python::handle<PyObject>(groupsTuple));
}

}