/*
jlayout - A Java code generator for GUI layout
Copyright (c) 2007-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	jlcheck.c	The jlcheck module in the jlayout program.
*/



/**	Inside the jlcheck module.
*/
#define JLCHECK_C	1
#include	"jl.h"

#line 50 "jlcheck.ctr"




/**	Ensure that each object has a class and a name assigned.
	@param	j	Jlayout job.
	@return	1 on success, 0 on error.
*/
static
int
all_objects_have_classes_and_names DK_P1(JLJ *,j)
{
  int back = 1;
  JLO *o;
  dksto_it_reset(j->o_it);
  while((o = (JLO *)dksto_it_next(j->o_it)) != NULL) {
    if(!(o->on)) {
      back = 0;
      j->errlineno = o->l_creation;
      jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 2);
    } else {
      if(!jlo_get_classname(o)) {
        
        back = 0;
        j->errlineno = o->l_creation;
        if(o->on) {
          jlmsg_log3(j, DK_LOG_LEVEL_ERROR, 3, o->on, 4);
        } else {
          jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 5);
        }
      }
    }
  }
  return back;
}



/**	Enumerate objects (assign a unique number to each object).
	@param	j	Jlayout job.
*/
static
void
enumerate_objects DK_P1(JLJ *,j)
{
  JLO *jlo;
  
  (j->o_main)->obj_no = 0UL;
  j->max_obj_no = 0UL;
  dksto_it_reset(j->o_it);
  while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) {
    j->max_obj_no += 1UL;
    jlo->obj_no = j->max_obj_no;
    
  }
  
}



/**	Set bit matrix entries for one objects dependencies.
	@param	j	Jlayout job.
	@param	o	Object to process.
	@param	bm	Bit matrix.
*/
static
void
create_entries_for_object DK_P3(JLJ *,j, JLO *,o, dk_bitmatrix_t *,bm)
{
  JCN *jcn; JLO *jlo;
  
  if(o->menubar) {
    dkbf_matrix_set(bm, o->obj_no, (o->menubar)->obj_no, 1);
  }
  if((o->c_st) && (o->c_it)) {
    dksto_it_reset(o->c_it);
    while((jcn = (JCN *)dksto_it_next(o->c_it)) != NULL) {
      if((jcn->o) && (jcn->t == JCN_OBJECT)) {
        jlo = jcn->o;
        dkbf_matrix_set(bm, o->obj_no, jlo->obj_no, 1);
        
        
      }
    }
  } 
}



/**	Find levels for objects.
	@param	jlj	Jlayout job.
	@param	l	Buffer for level numbers.
	@param	sz	Number of entries in \a l.
	@param	bm	Bit matrix containig object dependencies.
	@return	1 on success, 0 on error.
*/
static
int
find_levels DK_P4(JLJ *,jlj, unsigned long *,l, size_t, sz, dk_bitmatrix_t *,bm)
{
  int back = 1, cc = 0, can_set_level = 0;
  size_t i, j;
  unsigned long passno, *ulptr;
  
  for(i = 0; i < sz; i++) {
    if(dkbf_matrix_get(bm, i, i)) {
      back = 0;	
      /* ERROR: Circular dependencies */
      jlmsg_log1(jlj, DK_LOG_LEVEL_ERROR, 14);
    }
  }
  if(back) {
    /* initialize array */
    ulptr = l;
    for(i = 0; i < sz; i++) { *(ulptr++) = 0UL; }
    /* multiple passes to assign a level */
    cc = 1; passno = 1UL;
    while(cc) {				
      cc = 0;
      for(i = 0; i < sz; i++) {		
        if(l[i] == 0UL) {
	  can_set_level = 1;
	  for(j = 0; j < sz; j++) {
	    if(i != j) {		
	      if(dkbf_matrix_get(bm, i, j)) {
	        if(l[j] == 0UL) {	
		  can_set_level = 0;
		}
	      }
	    }
	  }
	  if(can_set_level) {
	    l[i] = passno; 
	    jlj->max_level = passno;
	  } else {
	    cc = 1;			
	  }
	}
      }
      /* count passes, we must not have more passes than objects */
      passno++;
      if(passno >= (unsigned long)sz) {
        if(cc) {			
	  cc = 0; back = 0;
	  /* Emergency stop, internal error */
	  jlmsg_log1(jlj, DK_LOG_LEVEL_ERROR, 15);
	}
      }
    }
  } 
  return back;
}



/**	Write object dependencies into bit matrix.
	@param	j	Jlayout job.
	@param	bm	Bit matrix.
*/
static
void
create_bit_matrix_entries DK_P2(JLJ *,j, dk_bitmatrix_t *,bm)
{
  JLO *jlo;
  
  create_entries_for_object(j, j->o_main, bm);
  dksto_it_reset(j->o_it);
  while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) {
    create_entries_for_object(j, jlo, bm);
  } 
}




#line 225 "jlcheck.ctr"

#line 226 "jlcheck.ctr"

#line 227 "jlcheck.ctr"

#line 228 "jlcheck.ctr"

#line 229 "jlcheck.ctr"

#line 230 "jlcheck.ctr"

#line 231 "jlcheck.ctr"

#line 232 "jlcheck.ctr"

#line 233 "jlcheck.ctr"

#line 234 "jlcheck.ctr"

#line 235 "jlcheck.ctr"

#line 236 "jlcheck.ctr"

#line 237 "jlcheck.ctr"

#line 238 "jlcheck.ctr"

#line 239 "jlcheck.ctr"

#line 240 "jlcheck.ctr"

#line 241 "jlcheck.ctr"

#line 242 "jlcheck.ctr"

#line 243 "jlcheck.ctr"

#line 244 "jlcheck.ctr"

#line 245 "jlcheck.ctr"

#line 246 "jlcheck.ctr"



/**	Levelize objects to find order of construction.
	@param	j	Jlayout job.
	@return	1 on success, 0 on error.
*/
static
int
levelize_objects DK_P1(JLJ *,j)
{
  int back = 0;
  unsigned long number_of_objects;
  size_t sz;
  unsigned long *levels = NULL;
  dk_bitmatrix_t *bm = NULL;
  JLO *jlo;
  
  number_of_objects = j->max_obj_no;
  number_of_objects++;
  sz = (size_t)number_of_objects;
  if((unsigned long)sz == number_of_objects) {
    levels = dk_new(unsigned long,sz);
    if(levels) {
      bm = dkbf_matrix_open(sz, sz);
      if(bm) {		
        create_bit_matrix_entries(j, bm);
	
#line 274 "jlcheck.ctr"
	dkbf_matrix_expand(bm);
	
#line 276 "jlcheck.ctr"
	back = find_levels(j, levels, sz, bm);
	(j->o_main)->obj_lvl = levels[0];
	dksto_it_reset(j->o_it);
	while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) {
	  jlo->obj_lvl = levels[jlo->obj_no];
	}
        dkbf_matrix_close(bm);
      } else {
        jlmsg_error_memory(j);
      }
      dk_delete(levels);
    } else {
      jlmsg_error_memory(j);
    }
  } else {
    /* ERROR: Too many objects */
    jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 17);
  } 
  return back;
}



/**	Check gridbag layout for one object.
	@param	j	Jlayout job.
	@param	jlo	Object to check.
	@return	1 on success, 0 on error.
*/
static
int
do_gbl_check DK_P2(JLJ *,j, JLO *,jlo)
{
  int back = 1;
  JCN *jcn; JLO *o;
  long x, y, bx, by, l;
  size_t sx, sy;
  long *tx = NULL, *ty = NULL;
  dk_bitfield_t *bfx1 = NULL, *bfy1 = NULL;	/* objects start in line/row */
  dk_bitfield_t *bfx2 = NULL, *bfy2 = NULL;	/* objects end in line/row */
  
  jlo->grid_layout_r = jlo->grid_layout_c = 0L;
  jlo->max_x = jlo->max_y = 0L;
  /* pass 1: find number of rows and columns */
  dksto_it_reset(jlo->c_it);
  while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) {
    switch(jcn->t) {
      case JCN_OBJECT: {
        if(jcn->o) {
	  o = jcn->o;
	  x = o->pos_x + (o->pos_w - 1L);
	  y = o->pos_y + (o->pos_h - 1L);
	  if(x > jlo->max_x) { jlo->max_x = x; }
	  if(y > jlo->max_y) { jlo->max_y = y; }
	}
      } break;
      default: {
        /* Error: Dont know how to add glue or separator */
      } break;
    }
  } 
  bx = jlo->max_x + 2L; by = jlo->max_y + 2L;
  
  sx = (size_t)bx; sy = (size_t)by;
  if(((long)sx == bx) && ((long)sy == by)) {
    jlo->distance_x = dkbf_open(bx);
    jlo->distance_y = dkbf_open(by);
    if((jlo->distance_x) && (jlo->distance_y)) {
      bfx1 = dkbf_open(bx); bfy1 = dkbf_open(by);
      bfx2 = dkbf_open(bx); bfy2 = dkbf_open(by);
      tx = dk_new(long,bx);
      ty = dk_new(long,by);
      if((bfx1) && (bfy1) && (bfx2) && (bfy2) && (tx) && (ty)) {
        dksto_it_reset(jlo->c_it);
	while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) {
	  switch(jcn->t) {
	    case JCN_OBJECT: {
	      if(jcn->o) {
	        o = jcn->o;
		dkbf_set(bfx1, o->pos_x, 1);
		dkbf_set(bfy1, o->pos_y, 1);
		x = o->pos_x + o->pos_w;
		y = o->pos_y + o->pos_h;
		dkbf_set(bfx2, x, 1);
		dkbf_set(bfy2, y, 1);
	      }
	    } break;
	  }
	}
	for(x = 0L; x < bx; x++) {
	  if(dkbf_get(bfx1, x)) {
	    if(dkbf_get(bfx2, x)) {
	      dkbf_set(jlo->distance_x, x, 1);
	      
	    }
	  }
	}
	dkbf_set(jlo->distance_x, 0, 1);
	dkbf_set(jlo->distance_x, (bx - 1L), 1);
	for(y = 0L; y < by; y++) {
	  if(dkbf_get(bfy1, y)) {
	    if(dkbf_get(bfy2, y)) {
	      dkbf_set(jlo->distance_y, y, 1);
	      
	    }
	  }
	}
	dkbf_set(jlo->distance_y, 0, 1);
	dkbf_set(jlo->distance_y, (by - 1L), 1);
	for(x = 0L; x < bx; x++) {
	  tx[x] = x;
	}
	for(y = 0L; y < by; y++) {
	  ty[y] = y;
	}
	for(x = 0L; x < bx; x++) {
	  if(dkbf_get(jlo->distance_x, x)) {
	    for(l = x; l < bx; l++) {
	      tx[l] += 1L;
	    }
	  }
	}
	for(y = 0L; y < by; y++) {
	  if(dkbf_get(jlo->distance_y, y)) {
	    for(l = y; l < by; l++) {
	      ty[l] += 1L;
	    }
	  }
	} 
	
#line 405 "jlcheck.ctr"
	
#line 406 "jlcheck.ctr"
	
#line 407 "jlcheck.ctr"
	
#line 408 "jlcheck.ctr"
	
#line 409 "jlcheck.ctr"
	
#line 410 "jlcheck.ctr"
	dksto_it_reset(jlo->c_it);
	while((jcn = (JCN *)dksto_it_next(jlo->c_it)) != NULL) {
	  switch(jcn->t) {
	    case JCN_OBJECT: {
	      if(jcn->o) {
	        o = jcn->o;
		o->cor_x = tx[o->pos_x];
		o->cor_y = ty[o->pos_y];
		x = tx[o->pos_x + (o->pos_w - 1L)];
		y = ty[o->pos_y + (o->pos_h - 1L)];
		if(x > jlo->cor_mx) { jlo->cor_mx = x; }
		if(y > jlo->cor_my) { jlo->cor_my = y; }
		o->cor_w = 1L + (x - o->cor_x);
		o->cor_h = 1L + (y - o->cor_y);


	      }
	    } break;
	  }
	}
	
      } else {
        back = 0;
	/* ERROR: Memory */
	jlmsg_error_memory(j);
      }
      if(bfx1) {
        dkbf_close(bfx1); bfx1 = NULL;
      }
      if(bfy1) {
        dkbf_close(bfy1); bfy1 = NULL;
      }
      if(bfx2) {
        dkbf_close(bfx2); bfx2 = NULL;
      }
      if(bfy2) {
        dkbf_close(bfy2); bfy2 = NULL;
      }
      if(tx) {
        dk_delete(tx); tx = NULL;
      }
      if(ty) {
        dk_delete(ty); ty = NULL;
      }
    } else {
      back = 0;
      /* ERROR: Memory */
      jlmsg_error_memory(j);
    }
  } else {	
    back = 0;
    /* ERROR: Too many rows or columns */
    jlmsg_log1(j, DK_LOG_LEVEL_ERROR, 54);
  }
  
  return back;
}



/**	Check gridbag layout for one object if the object uses gridbag layout.
	@param	j	Jlayout job.
	@param	jlo	Object to check.
	@return	1 on success, 0 on error.
*/
static
int
gbl_check DK_P2(JLJ *,j, JLO *,jlo)
{
  int back = 1;
  if(jlo->layout == LAYOUT_GRIDBAG) {
    back = do_gbl_check(j, jlo);
  }
  return back;
}



/**	Check all gridbag layouts.
	@param	j	Jlayout job.
	@return	1 on success, 0 on error.
*/
static
int
check_gridbag_layouts DK_P1(JLJ *,j)
{
  int back = 0;
  JLO *jlo;
  if(gbl_check(j, j->o_main)) {
    back = 1;
    dksto_it_reset(j->o_it);
    while((jlo = (JLO *)dksto_it_next(j->o_it)) != NULL) {
      if(!gbl_check(j, jlo)) {
        back = 0;
      }
    }
  }
  return back;
}



/**	Check input for consistency.
	@param	j	Jlayout job.
	@return 1 on success, 0 on error.
*/
int
jlcheck DK_P1(JLJ *,j)
{
  int back = 0;
  char *olderrfilename;
  
  olderrfilename = j->errfilename;
  j->errfilename = j->ifname;
  if(all_objects_have_classes_and_names(j)) {
    enumerate_objects(j);
    if(levelize_objects(j)) {
      if(check_gridbag_layouts(j)) {
        back = 1;
      }
    }
  }
  j->errfilename = olderrfilename;
  
  return back;
}


