void DynamicMaterial::setTextures(MaterialEditor* src) {
    if(!m_material) return;
    if(m_stream) m_stream->build();

    // Textures
    m_material->setTexture("diffuseArray", src->getDiffuseArray());
    m_material->setTexture("normalArray", src->getNormalArray());
    if(m_stream) {
        m_stream->setTexture("diffuseArray", src->getDiffuseArray());
        m_stream->setTexture("normalArray", src->getNormalArray());
        printf("Textures set\n");
    }

    // Bind all the maps
    typedef std::set<const char*> MapList;
    MapList maps;
    for(size_t i=0; i<m_layers.size(); ++i) {
        if(m_layers[i]->map  && m_layers[i]->map[0])  maps.insert( m_layers[i]->map );
        if(m_layers[i]->map2 && m_layers[i]->map2[0]) maps.insert( m_layers[i]->map2 );
        update(i);
    }
    for(MapList::iterator i=maps.begin(); i!=maps.end(); ++i) {
        EditableTexture* map = src->getMap(*i);
        if(map) {
            m_material->setTexture(*i, map->getTexture());
            if(m_stream) {
                if(map->getTextureStream()) m_stream->addStream(*i, map->getTextureStream());
                else m_stream->setOverlayTexture(*i, map->getTexture());
            }
        }
    }
}
// Set value from the map list.
String SetParametersObject(MapList params, bool last_param_array) {
    MapList::iterator it;
	MapList::iterator last_param = params.end();
	last_param--;

    String params_object = "";

	for (it = params.begin(); it != params.end(); it++) {
		AddPair(params_object, it->first, it->second, (last_param == it && last_param_array) ? false : true, (last_param != it) ? true : false);
	}

	return params_object;
}
bool DynamicMaterial::compile() {
    std::string source;

    // Inputs
    source +=
        "#version 130\n\n"
        "in vec3 worldNormal;\n"
        "in vec3 worldPos;\n\n"
        "uniform vec3 lightDirection;\n\n";

    // Samplers
    source +=
        "uniform sampler2DArray diffuseArray;\n"
        "uniform sampler2DArray normalArray;\n";

    // Maps
    typedef std::set<std::string> MapList;
    MapList maps;
    for(size_t i=0; i<m_layers.size(); ++i) {
        if(m_layers[i]->map  && m_layers[i]->map[0])  maps.insert( str(m_layers[i]->map) );
        if(m_layers[i]->map2 && m_layers[i]->map2[0]) maps.insert( str(m_layers[i]->map2) );
    }
    for(MapList::iterator i=maps.begin(); i!=maps.end(); ++i) source += "uniform sampler2D " + *i + "Map;\n";
    for(MapList::iterator i=maps.begin(); i!=maps.end(); ++i) source += "uniform vec4 " + *i + "Info;\n";
    source += "\n\n";

    // Variables
    for(size_t i=0; i<m_layers.size(); ++i) {
        std::string index = str(i);
        if(m_layers[i]->projection == PROJECTION_FLAT) source += "uniform vec2 scale" + index + ";\n";
        else source += "uniform vec3 scale" + index + ";\n";
        source += "uniform float opacity" + index + ";\n";
        if(m_layers[i]->type == LAYER_AUTO) source +=
                "uniform vec3 autoMin" + index + ";\n"
                "uniform vec3 autoMax" + index + ";\n"
                "uniform vec3 autoBlend" + index + ";\n";
    }

    source += "\n\n//Utility functions\n";

    // Functions
    source +=
        "vec4 sampleTriplanar(float map, vec3 coord, vec3 weights) {\n"
        "	vec4 c = vec4(coord.xyz, map);\n"
        "	return texture(diffuseArray, c.yzw)*weights.xxxx + texture(diffuseArray, c.zxw)*weights.yyyy + texture(diffuseArray, c.xyw)*weights.zzzz;\n"
        "}\n";

    source +=
        "vec4 sampleTriplanerNormal(float map, vec3 coord, vec3 normal, vec3 weights) {\n"
        "	vec4 c = vec4(coord.xyz, map);\n"
        "	vec4 nX = texture(normalArray, c.yzw);\n"
        "	vec4 nY = texture(normalArray, c.zxw);\n"
        "	vec4 nZ = texture(normalArray, c.xyw);\n"
        "	nX.xyz = nX.xyz * 2.0 - 1.0;\n"
        "	nY.xyz = nY.xyz * 2.0 - 1.0;\n"
        "	nZ.xyz = nZ.xyz * 2.0 - 1.0;\n"
        "	if(dot(nX.xyz, normal)<0.0) nX.x = -nX.x;\n"
        "	if(dot(nY.xyz, normal)<0.0) nY.y = -nY.y;\n"
        "	if(dot(nZ.xyz, normal)<0.0) nZ.z = -nZ.z;\n"
        "	vec3 result = normalize(nX.xyz*weights.xxx + nY.xyz*weights.yyy + nZ.xyz*weights.zzz);\n"
        "	return vec4(result, nX.w*weights.x + nY.w*weights.y + nZ.w*weights.z);\n"
        "}\n";

    source +=
        "float getAutoWeight(vec3 value, vec3 vmin, vec3 vmax, vec3 vblend) {\n"
//	"	vec3 ctr = (vmin + vmax) * 0.5;\n"
//	"	vec3 r = smoothstep(ctr - vmin, ctr - vmin + vblend, abs(value - ctr));\n"
        "	vec3 r = smoothstep(vmin-vblend, vmin, value) * smoothstep(vmax+vblend, vmax, value);\n"
        "	return r.x * r.y * r.z;\n"
        "}\n";

    source +=
        "vec4 sampleMap(sampler2D map, vec4 info, vec2 coord) {\n"
        "	coord = (coord - info.xy) * info.zw;\n"
        "	return texture(map, coord);\n"
        "}\n";

    source +=
        "vec4 sampleDiffuse(float map, vec2 coord) {\n"
        "	return texture(diffuseArray, vec3(coord, map));\n"
        "}\n";

    source +=
        "vec4 sampleNormal(float map, vec2 coord) {\n"
        "	vec4 n = texture(diffuseArray, vec3(coord, map));\n"
        "	return vec4(n.xyz * 2.0 - 1.0, n.w);\n"
        "}\n\n\n";

    // Main function
    source +=
        "// Main shader function\n"
        "void main() {\n"
        "	vec4 diffuse = vec4(1,1,1,1);\n"
        "	vec4 normal = vec4(0,1,0,0);\n"
        "	float gloss = 0.0;\n"
        "	float height;\n"
        "	float weight;\n"
        "	vec4 diff, norm;\n"
        "	vec3 triplanar = max( (abs(worldNormal) - 0.2) * 0.7, 0.0);\n"
        "	triplanar /= dot(triplanar, vec3(1,1,1));\n"
        "	vec3 autoValue = vec3(worldPos.y, 1.0 - worldNormal.y, 0.0);\n"
        "	\n";

    // Vertical projection data
    source +=
        "	vec3 vertical = vec3(triplanar.x, 0, triplanar.z);\n"
        "	vertical /= triplanar.y>0.99? 1: dot(vertical, vec3(1,1,1));\n\n";



    // Sample maps
    if(!maps.empty()) {
        source +=  "	// Sample maps\n";
        for(MapList::iterator i=maps.begin(); i!=maps.end(); ++i) {
            source +=  "	vec4 " + *i + "Sample = sampleMap(" + *i + "Map, " + *i + "Info, worldPos.xz);\n";
        }
        source += "\n";
    }

    // Apply layers
    const std::string rgba[] = { "r", "g", "b", "a" };
    for(size_t i=0; i<m_layers.size(); ++i) {
        std::string index = str(i);
        MaterialLayer* layer = m_layers[i];
        Colour colour(layer->colour);
        bool valid = true;

        source +=  "	// Layer " + str(layer->name) + "\n";

        switch(layer->type) {
        case LAYER_AUTO:
            source +=  "	weight = getAutoWeight(autoValue, autoMin"+index+", autoMax"+index+", autoBlend"+index+");\n";
            break;

        case LAYER_WEIGHT:
            if(!layer->map || !layer->map[0]) valid = false;
            else if(layer->mapData<4)
                source +=  "	weight = " + str(layer->map) + "Sample." + rgba[layer->mapData] + ";\n";
            else
                source +=  "	weight = 1.0 - dot(" + str(layer->map) + "Sample, vec4(1,1,1,1));\n";
            break;

        case LAYER_COLOUR:
            if(!layer->map || !layer->map[0]) valid = false;
            else source +=  "	diff = " + str(layer->map) + "Sample;\n	weight = 1.0;\n";
            break;
        case LAYER_INDEXED:
            // ToDo this one
            break;
        }
        if(!valid) {
            printf("Error: Layer %s references an invalid map\n", (const char*)layer->name);
            continue;
        }

        // Sample textures
        if(layer->type != LAYER_COLOUR) {
            if(layer->texture < 0) source +=
                    "	diff = vec4(" + str(colour.r) + ", " + str(colour.g) + ", " + str(colour.b) + ", 0);\n"
                    "	norm = vec4(0, 0, 1, 0);\n";
            else if(layer->projection == PROJECTION_VERTICAL) source +=
                    "	diff = sampleTriplanar("+str(layer->texture)+".0, worldPos * scale"+index+", vertical);\n"
                    "	norm = sampleTriplanerNormal("+str(layer->texture)+".0, worldPos * scale"+index+", worldNormal, vertical);\n";
            else if(layer->projection == PROJECTION_TRIPLANAR) source +=
                    "	diff = sampleTriplanar("+str(layer->texture)+".0, worldPos * scale"+index+", triplanar);\n"
                    "	norm = sampleTriplanerNormal("+str(layer->texture)+".0, worldPos * scale"+index+", worldNormal, triplanar);\n";
            else source +=
                    "	diff = sampleDiffuse("+str(layer->texture)+".0, worldPos.xz * scale"+index+");\n"
                    "	norm = sampleNormal("+str(layer->texture)+".0, worldPos.xz * scale"+index+");\n";
        }


        // Blending
        switch(layer->blend) {
        case BLEND_NORMAL:
        case BLEND_HEIGHT:
            source +=
                "	diffuse = mix(diffuse, diff, weight * opacity" + index + ");\n"
                "	normal = mix(normal, norm, weight * opacity" + index + ");\n";
            break;
        case BLEND_MULTIPLY:
            source +=
                "	diffuse *= mix(vec4(1,1,1,1), diff, weight * opacity" + index + ");\n";
            break;
        case BLEND_ADD:
            source +=
                "	diffuse += diff * weight * opacity" + index + ";\n";
            break;
        }

        source += "\n";
    }

    // Lighting (basic diffuse);
    source +=  "	// Lighting\n";
    source +=  "	gl_FragColor = diffuse * 0.1 + diffuse * 0.9 * dot(worldNormal, lightDirection);\n";
    source +=  "	gl_FragColor = vec4(diffuse.rgb, 1);\n";
    source += "}\n";

    // Vertex shader - todo: add concavity attribute
    static const char* vertexShader =
        "varying vec3 worldNormal;\n"
        "varying vec3 worldPos;\n"
        "void main() {\n"
        "	gl_Position = ftransform();\n"
        "	worldPos = gl_Vertex.xyz;\n"
        "	worldNormal = gl_Normal;\n"
        "}\n";

    // Compile shader
    const char* fragmentShader = source.c_str();
    VertexShader vert = Shader::createVertex(&vertexShader);
    FragmentShader frag = Shader::createFragment(&fragmentShader);
    Shader shader = Shader::link(vert, frag);


    // Setup material
    if(!m_material) m_material = new Material();
    m_material->setShader(shader);

    // Streamed material
    if(m_streaming) {
        if(!m_stream) m_stream = new MaterialStream(m_material);
        m_stream->updateShader();
    }

    // Initialise variables
    for(size_t i=0; i<m_layers.size(); ++i) {
        update(i);
    }


    // DEBUG: save generated shader
    FILE* fp = fopen("shader.glsl", "w");
    if(fp) fwrite(source.c_str(), 1, source.length(), fp);
    if(fp) fclose(fp);
    printf("Compiled shader\n");


    m_needsCompile = false;
    return false;
}
void TouchKeyboard::reload_current_layout()
{
	this->free_button_vram();
	for( TextButtonSetStorage::iterator ki=this->keys.begin(); ki!=this->keys.end(); ki++ )
	{
		if( *ki )
		{
			delete *ki;
		}
	}
	this->keys.clear();
	this->text_buttons.clear();
	this->text_buttons.push_back( &this->reference_key );
	this->text_buttons.push_back( &this->layout_minus );
	this->text_buttons.push_back( &this->layout_label );
	this->text_buttons.push_back( &this->layout_plus );
	
	this->close_db();
	if( this->current_layout != this->layouts.end() )
	{
		int rc;
		int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX;
		if( (rc = sqlite3_open_v2( this->current_layout->c_str(), &db, flags, 0))!=SQLITE_OK )
		{
			std::stringstream msg;
			msg << sqlite3_errmsg(db) << ": " << rc << " (" << *this->current_layout << ")";
			this->close_db();
			throw ERROR( msg.str() );
		}
		std::string stmt = "select key, value from settings";
		MapList result;
		if( (rc = sqlite3_exec(db, stmt.c_str(), map_list_callback, &result, 0))!=SQLITE_OK )
		{
			std::stringstream msg;
			msg << sqlite3_errmsg(db) << " (" << rc << "), in statement: " << stmt;
			throw ERROR( msg.str() );
		}
		for( MapList::iterator ri=result.begin(); ri!=result.end(); ri++ )
		{
			if( (*ri)["key"]=="description" ) this->current_layout_description = (*ri)["value"];
			if( (*ri)["key"]=="language" ) this->current_layout_language = (*ri)["value"];
			if( (*ri)["key"]=="keys" ) this->current_layout_keys = (*ri)["value"];
			if( (*ri)["key"]=="dead_keys" ) this->current_layout_dead_keys = (*ri)["value"];
		}
	}
	this->layout_label.text = this->current_layout_language;
	StringList keyboard_characters;
	utf8_to_utf8_char_list( (const unsigned char*)this->current_layout_keys.c_str(), keyboard_characters );
	
	int key_x_start = 1;
	int key_x_offset = this->reference_key.sensor_width+1;
	int key_x = key_x_start;
	int key_y_start = 50;
	int key_y_offset = this->reference_key.sensor_height+1;
	int key_y = key_y_start;
	for( StringList::iterator kci = keyboard_characters.begin();
		kci != keyboard_characters.end(); kci++ )
	{
		if( *kci=="\n" )
		{
			key_x = key_x_start;
			key_y += key_y_offset;
			continue;
		}
		if( *kci=="\t" )
		{
			key_x += key_x_offset / 2;
			continue;
		}
		TextButton* new_key = new TextButton( this->reference_key );
		new_key->hidden = false;
		new_key->owns_bg_vram = false;
		new_key->owns_text_vram = true;
		new_key->text = *kci;
		if( new_key->text == "ˇ" )
		{
			new_key->text_y_offset += 3;
		}
		else if( new_key->text == "¯" || new_key->text == "´"
			|| new_key->text == "`" )
		{
			new_key->text_y_offset += 3;
			new_key->font_size += 2;
		}
		new_key->x = key_x;
		new_key->y = key_y;
		this->text_buttons.push_back( new_key );
		this->keys.insert( new_key );
		key_x += key_x_offset;
	}
}
ButtonAction TouchKeyboard::handle_button_pressed( TextButton* text_button )
{
	if( this->keys.find(text_button) != this->keys.end() )
	{
		std::string button_text = this->handle_key_pressed( text_button->text );
		// FIXME: besser db-patterns abfragen!
		if( this->current_layout_dead_keys.find(button_text)!=std::string::npos )
		{
			this->modifier = button_text;
		}
		else if( button_text == "<" )
		{
			if( this->modifier.length() )
			{
				this->modifier = "";
			}
			else if( this->written_chars.size() )
			{
				this->written_chars.pop_back();
				this->handle_text_changed();
			}
		}
		else
		{
			if( this->modifier.length() )
			{
				button_text = this->modifier + button_text;
				
				if( db )
				{
					int rc;
					button_text = replace_pattern( button_text, "'", "\'" );
					std::string stmt = "select result from modifiers where pattern='"+button_text+"'";
					MapList result;
					if( (rc = sqlite3_exec(db, stmt.c_str(), map_list_callback, &result, 0))!=SQLITE_OK )
					{
						std::stringstream msg;
						msg << sqlite3_errmsg(db) << " (" << rc << "), in statement: " << stmt;
						throw ERROR( msg.str() );
					}
					for( MapList::iterator ri=result.begin(); ri!=result.end(); ri++ )
					{
						button_text = (*ri)["result"];
						break;
					}
				}
			}
			utf8_to_utf8_char_list( (const unsigned char*)button_text.c_str(), this->written_chars );
			this->modifier = "";
			this->handle_text_changed();
		}
		return BUTTON_ACTION_PRESSED | BUTTON_ACTION_SCREEN_SUB;
	}
	else if( text_button == &this->layout_minus )
	{
		if( this->current_layout != this->layouts.begin() )
		{
			this->current_layout--;
		}
		else if( this->layouts.end() != this->layouts.begin() )
		{
			this->current_layout = this->layouts.end();
			this->current_layout--;
		}
		this->init_button_vram();
		this->program.config->set( "touch_keyboard.current_layout", *this->current_layout );
		 // WARNING: button release handler will crash because of iterator invalidation if STOP_HANDLER ist not set:
		return BUTTON_ACTION_PRESSED | BUTTON_ACTION_SCREEN_SUB | BUTTON_ACTION_STOP_HANDLER;
	}
	else if( text_button == &this->layout_plus )
	{
		if( this->layouts.size() && &*this->current_layout != &*this->layouts.rbegin() )
		{
			this->current_layout++;
		}
		else
		{
			this->current_layout = this->layouts.begin();
		}
		this->init_button_vram();
		this->program.config->set( "touch_keyboard.current_layout", *this->current_layout );
		 // WARNING: button release handler will crash because of iterator invalidation when STOP_HANDLER ist not set:
		return BUTTON_ACTION_PRESSED | BUTTON_ACTION_SCREEN_SUB | BUTTON_ACTION_STOP_HANDLER;
	}
	
	return this->ButtonProvider::handle_button_pressed( text_button );
}